【问题标题】:Download HTTP Website through SOCKS5 using Winsock2使用 Winsock2 通过 SOCKS5 下载 HTTP 网站
【发布时间】:2016-12-09 14:58:15
【问题描述】:

正如标题所说,我想通过使用 Winsock2 的 SOCKS5 代理服务器下载网站源。该程序是用MinGW编译的。

使用的文档来自RFC 1928

为了向你介绍这个主题,为了在 SOCKS5 服务器和 webserver 之间建立连接,我必须向 socks 服务器发送两个请求。如果这两个都成功了,那么我可以使用带有 send() 和 recv() 的套接字句柄通过我们刚刚创建的隧道与 webserver 交互。

根据 RFC,第一个数据包看起来像:

+----+----------+----------+
|VER | NMETHODS | METHODS  |
+----+----------+----------+
| 1  |    1     | 1 to 255 |
+----+----------+----------+

我只想要一种方法,一种没有身份验证的方法。 而且我已经以这种方式构建了数据包:

char *initPacket1 = new char[3];
int initPacket1Length = sizeof(initPacket1) / sizeof(initPacket1[0]);
    
initPacket1[0] = 5;  //SOCKS Version. [VER]
initPacket1[1] = 1;  //No. of methods [NMETHODS]
initPacket1[2] = 0;  //No auth required [X’00’]

if(send(hSocketSock, initPacket1, initPacket1Length, 0) == SOCKET_ERROR)
{
    return -1;
}

响应应该是一个包含版本和方法的 2 字节数据包。 第一个问题是这一步偶尔会失败。更具体地说,recv() 函数有效,但接收到的字节无效。但它只会发生几次。

接下来,我必须构建 第二个数据包,它告诉代理服务器连接到目标网站。我必须构建的数据包具有以下形式:

+----+-----+-------+------+----------+----------+
|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X’00’ |   1  | Variable |    2     |
+----+-----+-------+------+----------+----------+

以及我构建该数据包的方式:

char *initPacket2 = new char[10];
int initPacket2Length = sizeof(initPacket2) / sizeof(initPacket2[0]);

initPacket2[0] = 5; //SOCKS Version;
initPacket2[1] = 1; //1 = CONNECT, 2 = BIND, 3 = UDP ASSOCIATE;
initPacket2[2] = 0; //Reserved byte
initPacket2[3] = 1; //1 = IPv4, 3 = DOMAINNAME, 4 = IPv6

memcpy(initPacket2 + 4, &dest.sin_addr.S_un.S_addr, 4);
memcpy(initPacket2 + 8, &dest.sin_port, 2);

if(send(hSocketSock, initPacket2, initPacket2Length, 0) == SOCKET_ERROR)
{
    return -1;
}

第二个大问题是每次响应字段都是“07”,表示“不支持命令”。

我已经修改了第二个数据包的第二个字节,但没有成功。

很可能我在构建数据包时错过了一些东西,我不知道是什么。

完整程序: ma​​in.cpp

#include <winsock2.h>
#include <string>
#include <iostream>

#include "util.hpp"

using namespace std;

int main(void)
{
    //SOCKS5 info
    u_short sockPort = 45554;
    std::string sockIp = "xx.xx.xx.xx";

    //Destination info
    u_short destPort = 80;
    std::string destIPorURL = "checkip.dyndns.com";

    //Check to see if winsock2 is available
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,0), &wsaData)==0)
    {
        if (LOBYTE(wsaData.wVersion) < 2)
        {
            cout << "WSA Version error!";
            return -1;
        }
    }
    else
    {
        cout << "WSA Startup Failed";
        return -1;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    //Init sockaddr for SOCKS5
    cout << "Initialize sock_addr for socks4 connection...";
    sockaddr_in sock;
    sock.sin_family = AF_INET;                      // host byte order
    sock.sin_port = htons( sockPort );              // short, network byte order
    if(!utils::getHostIP(sock.sin_addr.S_un.S_addr, sockIp)) // Write ip address in the right format
    {
        cout << "fail";
        return -1;
    }
    cout << "done" << endl;

    //Creating socket handler
    cout << "Creating socket handler...";
    SOCKET hSocketSock = INVALID_SOCKET;
    if( (hSocketSock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
    {
        cout << "fail";
        return -1;
    }
    cout << "done" << endl;

    /////////////////////////////////////////////////////////////////////////////////////////////
    //Init sockaddr for destination server
    cout << "Initialize sock_addr for destination server...";
    sockaddr_in dest;
    dest.sin_family = AF_INET;
    dest.sin_port = htons( destPort );
    if(!utils::getHostIP(dest.sin_addr.S_un.S_addr, destIPorURL)) // Write ip address in the right format
    {
        cout << "fail";
        return -1;
    }
    memset( &(dest.sin_zero), '\0', 8 );
    cout << "done" << endl;

    ////////////////////////////////////////////////////////////////////////////////////////////////
    //Time to connect to SOCKS5 server
    cout << "Connecting to sock server...";
    if(connect(hSocketSock, reinterpret_cast<sockaddr *>(&sock), sizeof(sock)) != 0)
    {
        cout << "failed";
        return -1;
    }
    cout << "done" << endl;

    //Documentation: https://tools.ietf.org/pdf/rfc1928.pdf

    //We have to send first packet which tell to SOCKS5 to enter on
    //sub-negociation mode so we can connect to actual destination server

    //    The client connects to the server, and sends a version
    //    identifier/method selection message:
    //    +----+----------+----------+
    //    |VER | NMETHODS | METHODS  |
    //    +----+----------+----------+
    //    | 1  |    1     | 1 to 255 |
    //    +----+----------+----------+

    //Allocate space for the first initialize packet and his replay
    char *initPacket1 = new char[3];
    int initPacket1Length = sizeof(initPacket1) / sizeof(initPacket1[0]);

    char reply1[2];
    memset( &reply1, '\0' , strlen((const char *)reply1) );

    initPacket1[0] = 5;  //SOCKS Version. [VER]
    initPacket1[1] = 1;  //No. of methods [NMETHODS]
    initPacket1[2] = 0;  //No auth required [X’00’]

    //Now we are sending the packet we just created
    cout << "Sending first init packet...";
    if(send(hSocketSock, initPacket1, initPacket1Length, 0) == SOCKET_ERROR)
    {
        cout << "failed";
        return -1;
    }
    cout << "done" << endl;

    //And our expected replay format:
    //
    //    The server selects from one of the methods given in METHODS, and
    //    sends a METHOD selection message:
    //    +----+--------+
    //    |VER | METHOD |
    //    +----+--------+
    //    | 1  |   1    |
    //    +----+--------+

    //Receiving response from server
    cout << "Waiting for response...";
    if(recv(hSocketSock, reply1, 2, 0) == SOCKET_ERROR)
    {
        cout << "failed";
        return -1;
    }
    cout << "done" << endl;

    //reply[0] = our version
    //reply[1] = out method. [X’00’ NO AUTHENTICATION REQUIRED]
    if( !(reply1[0] == 5 && reply1[1] == 0) )
    {
        cout << "Error: bad result on reply:" << (int)reply1[0] << " " << (int)reply1[1] << endl;
        return -1;
    }

    //We can now delete forst packet
    delete[] initPacket1;


    //We have to build initialize packet. This will transmit to SOCKS5 server
    //the web server we want to connect to.
    //
    //    The SOCKS request is formed as follows:
    //
    //    +----+-----+-------+------+----------+----------+
    //    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    //    +----+-----+-------+------+----------+----------+
    //    | 1  |  1  | X’00’ |   1  | Variable |    2     |
    //    +----+-----+-------+------+----------+----------+
    //
    //    Where:
    //    o VER protocol version: X’05’
    //    o CMD
    //    o CONNECT X’01’
    //    o BIND X’02’
    //    o UDP ASSOCIATE X’03’
    //    o RSV RESERVED
    //    o ATYP address type of following address
    //    o IP V4 address: X’01’
    //    o DOMAINNAME: X’03’
    //    o IP V6 address: X’04’
    //    o DST.ADDR desired destination address
    //    o DST.PORT desired destination port in network octet
    //    order

    //Building that packet
    char *initPacket2 = new char[10];
    int initPacket2Length = sizeof(initPacket2) / sizeof(initPacket2[0]);

    char reply2[16];
    int reply2Length = sizeof(reply2) / sizeof(reply2[0]);

    initPacket2[0] = 5; //SOCKS Version;
    initPacket2[1] = 1; //1 = CONNECT, 2 = BIND, 3 = UDP ASSOCIATE;
    initPacket2[2] = 0; //Reserved byte
    initPacket2[3] = 1; //1 = IPv4, 3 = DOMAINNAME, 4 = IPv6

    memcpy(initPacket2 + 4, &dest.sin_addr.S_un.S_addr, 4);
    memcpy(initPacket2 + 8, &dest.sin_port, 2);

    //Send the second init packet to server. This will inform the SOCKS5 server
    //what is our target.
    cout << "Sending second init packet...";
    if(send(hSocketSock, initPacket2, initPacket2Length, 0) == SOCKET_ERROR)
    {
        cout << "failed";
        return -1;
    }
    cout << "done" << endl;

    //Reading the response
    //Expected response format:

    //    The SOCKS request information is sent by the client as soon as it has
    //    established a connection to the SOCKS server, and completed the
    //    authentication negotiations. The server evaluates the request, and
    //    returns a reply formed as follows:

    //    +----+-----+-------+------+----------+----------+
    //    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    //    +----+-----+-------+------+----------+----------+
    //    | 1  |  1  | X’00’ |  1   | Variable |   2      |
    //    +----+-----+-------+------+----------+----------+
    //                              Where:
    //    o VER protocol version: X’05’
    //    o REP Reply field:
    //        o X’00’ succeeded
    //        o X’01’ general SOCKS server failure
    //        o X’02’ connection not allowed by ruleset
    //        o X’03’ Network unreachable
    //        o X’04’ Host unreachable
    //        o X’05’ Connection refused
    //        o X’06’ TTL expired
    //        o X’07’ Command not supported
    //        o X’08’ Address type not supported
    //        o X’09’ to X’FF’ unassigned
    //    ......................................

    cout << "Waiting for response...";
    if(recv(hSocketSock, reply2, reply2Length, 0) == SOCKET_ERROR)
    {
        cout << "failed";
        return -1;
    }
    cout << "done" << endl;

    cout << "Returned packets: " << endl;
    cout << "VER: " << (int)reply2[0] << endl;
    cout << "REP: " << (int)reply2[1] << endl;
    cout << "RSV: " << (int)reply2[2] << endl;
    cout << "ATY: " << (int)reply2[3] << endl;
    cout << endl;
}

返回的字节 REP 是 07,但我需要一个 0 才能继续。

我附上了一个有帮助功能的源文件,以防你想测试。

util.hpp

#include <winsock2.h>
#include <string>
#include <algorithm>
#include <vector>

namespace utils
{
    std::string getHostFromUrl(std::string &url);
    bool getHostIP(unsigned long &ipAddr, std::string urlOrHostnameOrIp);

    namespace IPAddr
    {
        bool isValidIPv4(std::string &ip);
        std::string reverseIpAddress(std::string ip);
        std::string decimalToDottedIp(unsigned long ip);
        unsigned long stripToDecimal(std::string &ip);
    }

    namespace strings
    {
        std::vector<std::string> split(std::string &s, char delim);
        std::string removeSubstrs(std::string &source, std::string pattern);
    }
};

util.cpp

#include <stdexcept>
#include <iostream>
#include <sstream>
#include <stdio.h>

#include "util.hpp"

#define cout std::cout
#define endl std::endl

/////////////////////////////////////////////////////////////////////////////////////
//   _   _                                                         _   _ _
//  | \ | | __ _ _ __ ___   ___  ___ _ __   __ _  ___ ___    _   _| |_(_) |___
//  |  \| |/ _` | '_ ` _ \ / _ \/ __| '_ \ / _` |/ __/ _ \  | | | | __| | / __|
//  | |\  | (_| | | | | | |  __/\__ \ |_) | (_| | (_|  __/  | |_| | |_| | \__ \
//  |_| \_|\__,_|_| |_| |_|\___||___/ .__/ \__,_|\___\___|   \__,_|\__|_|_|___/
//                                  |_|
/////////////////////////////////////////////////////////////////////////////////////

bool utils::getHostIP(unsigned long &ipAddr, std::string url)
{
    HOSTENT *pHostent;
    std::string hostname = getHostFromUrl(url);

    if( utils::IPAddr::isValidIPv4(hostname) )
    {
        //IP Address must be reversed in order to be compatible with sockAddr.sin_addr.S_un.S_addr
        //example: 192.168.1.2 => 2.1.168.192
        hostname = utils::IPAddr::reverseIpAddress(hostname);
        ipAddr =  utils::IPAddr::stripToDecimal(hostname);
        return true;
    }

    if (!(pHostent = gethostbyname(hostname.c_str())))
    {
        return false;
    }

    if (pHostent->h_addr_list && pHostent->h_addr_list[0])
    {
        ipAddr = *reinterpret_cast<unsigned long *>(pHostent->h_addr_list[0]);
        return true;
    }
    return false;
}

std::string utils::getHostFromUrl(std::string &url)
{
    std::string urlcopy = url;

    urlcopy = utils::strings::removeSubstrs(urlcopy, "http://");
    urlcopy = utils::strings::removeSubstrs(urlcopy, "www.");
    urlcopy = utils::strings::removeSubstrs(urlcopy, "https://");
    urlcopy = urlcopy.substr(0, urlcopy.find("/"));

    return urlcopy;
}

//   ___  ____        _        _      _
// | _ _||  _ \      / \    __| |  __| | _ __  ___  ___  ___
//   | | | |_) |    / _ \  / _` | / _` || '__|/ _ \/ __|/ __|
//   | | |  __/    / ___ \| (_| || (_| || |  |  __/\__ \\__ \
//  |___||_|      /_/   \_\\__,_| \__,_||_|   \___||___/|___/

bool utils::IPAddr::isValidIPv4(std::string &ipv4)
{
    const std::string address = ipv4;

    std::vector<std::string> arr;
    int k = 0;
    arr.push_back(std::string());
    for (std::string::const_iterator i = address.begin(); i != address.end(); ++i)
    {
        if (*i == '.')
        {
            ++k;
            arr.push_back(std::string());
            if (k == 4)
            {
                return false;
            }
            continue;
        }
        if (*i >= '0' && *i <= '9')
        {
            arr[k] += *i;
        }
        else
        {
            return false;
        }
        if (arr[k].size() > 3)
        {
            return false;
        }
    }

    if (k != 3)
    {
        return false;
    }
    for (int i = 0; i != 4; ++i)
    {
        const char* nPtr = arr[i].c_str();
        char* endPtr = 0;
        const unsigned long a = ::strtoul(nPtr, &endPtr, 10);
        if (nPtr == endPtr)
        {
            return false;
        }
        if (a > 255)
        {
            return false;
        }
    }
    return true;
}

std::string utils::IPAddr::reverseIpAddress(std::string ip)
{
    std::vector<std::string> octeti = utils::strings::split(ip, '.');
    return (octeti[3] + "." + octeti[2] + "." + octeti[1] + "." + octeti[0]);
}

unsigned long utils::IPAddr::stripToDecimal(std::string &ip)
{
    unsigned long a,b,c,d,base10IP;
    sscanf(ip.c_str(), "%lu.%lu.%lu.%lu", &a, &b, &c, &d);

    // Do calculations to convert IP to base 10
    a *= 16777216;
    b *= 65536;
    c *= 256;
    base10IP = a + b + c + d;

    return base10IP;
}

std::string utils::IPAddr::decimalToDottedIp(unsigned long ipAddr)
{
    unsigned short a, b, c, d;
    std::ostringstream os ;
    std::string ip = "";

    a = (ipAddr & (0xff << 24)) >> 24;
    b = (ipAddr & (0xff << 16)) >> 16;
    c = (ipAddr & (0xff << 8)) >> 8;
    d = ipAddr & 0xff;

    os << d << "." << c << "." << b << "." << a;
    ip = os.str();

    return ip;
}

//   ____   _          _
//  / ___| | |_  _ __ (_) _ __    __ _  ___
//  \___ \ | __|| '__|| || '_ \  / _` |/ __|
//   ___) || |_ | |   | || | | || (_| |\__ \
//  |____/  \__||_|   |_||_| |_| \__, ||___/
//                               |___/

std::vector<std::string> utils::strings::split(std::string &s, char delim)
{
    std::vector<std::string> elems;

    std::stringstream ss;
    ss.str(s);
    std::string item;
    while (std::getline(ss, item, delim))
    {
        elems.push_back(item);
    }
    return elems;
}

std::string utils::strings::removeSubstrs(std::string &input, std::string pattern)
{
    std::string source = input;
    std::string::size_type n = pattern.length();

    for (std::string::size_type i = source.find(pattern); i != std::string::npos; i = source.find(pattern))
    {
        source.erase(i, n);
    }
    return source;
}

【问题讨论】:

    标签: c++ sockets winsock2 socks


    【解决方案1】:

    您对 initPacket1LengthinitPacket2Length 的计算是错误的,因此它们都被分别设置为 4 而不是 3 和 10。这是因为您将 char* 指针传递给 sizeof 而不是 char[] 缓冲区,正如您所期望的(任何指针类型的 sizeof() 在 32 位系统上为 4,在 64 位系统上为 8)。

    这意味着第一个数据包发送 4 个字节而不是 3 个字节,但您没有分配 4 个字节,因此您在第 4 个字节发送垃圾(幸运的是,您的代码没有完全崩溃)。由于 SOCKS 服务器最初只期望 3 个字节,因此第 4 个字节和随后的 4 个字节被解释为失败的格式错误的 5 字节命令。

    由于您在分配缓冲区时使用硬编码大小,因此您应该硬编码长度以匹配。

    //int initPacket1Length = sizeof(initPacket1) / sizeof(initPacket1[0]);
    int initPacket1Length = 3;
    

    //int initPacket2Length = sizeof(initPacket2) / sizeof(initPacket2[0]);
    int initPacket2Length = 10;
    

    另外,当memset() 使用固定缓冲区时,使用strlen() 来计算大小是完全错误的。你应该改用sizeof

    //memset( &reply1, '\0' , strlen((const char *)reply1) );
    memset( &reply1, '\0' , sizeof(reply1));
    

    您还忽略了send()recv() 的返回值。它们返回实际发送/接收的字节数。但是您只检查失败的返回值,而不是成功。 TCP 是一种流式传输,send()recv() 可以发送/接收的字节数少于请求的字节数,因此您必须考虑到这一点。

    话虽如此,请尝试更多类似的东西:

    #include <winsock2.h>
    #include <string>
    #include <iostream>
    
    #include "util.hpp"
    
    using namespace std;
    
    int sendData(SOCKET s, const void *buffer, int buflen)
    {
        const char *pbuf = (const char*) buffer;
        while (buflen > 0)
        {
            int numSent = send(s, pbuf, buflen, 0);
            if (numSent == SOCKET_ERROR)
                return SOCKET_ERROR;
            pbuf += numSent;
            buflen -= numSent;
        }
        return 1;
    }
    
    int recvData(SOCKET s, void *buffer, int buflen)
    {
        char *pbuf = (char*) buffer;
        while (buflen > 0)
        {
            int numRecv = recv(s, pbuf, buflen, 0);
            if (numRecv == SOCKET_ERROR)
                return SOCKET_ERROR;
            if (numRecv == 0)
                return 0;
            pbuf += numRecv;
            buflen -= numRecv;
        }
        return 1;
    }
    
    int main(void)
    {
        //SOCKS5 info
        u_short sockPort = 45554;
        std::string sockIp = "xx.xx.xx.xx";
    
        //Destination info
        u_short destPort = 80;
        std::string destIPorHost = "checkip.dyndns.com";
    
        //Check to see if winsock2 is available
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0)
        {
            cout << "WSA Startup Failed";
            return -1;
        }
    
        if (LOBYTE(wsaData.wVersion) < 2)
        {
            cout << "WSA Version error!";
            return -1;
        }
    
        ///////////////////////////////////////////////////////////////////////////////////////////
        //Init sockaddr for SOCKS5
        cout << "Initialize sock_addr for socks5 connection...";
        sockaddr_in sock;
        sock.sin_family = AF_INET;                      // host byte order
        sock.sin_port = htons(sockPort);                // short, network byte order
        if (!utils::getHostIP(sock.sin_addr.S_un.S_addr, sockIp)) // Write ip address in the right format
        {
            cout << "fail";
            return -1;
        }
        cout << "done" << endl;
    
        /////////////////////////////////////////////////////////////////////////////////////////////
        //Init sockaddr for destination server
        cout << "Initialize sockaddr for destination server...";
        sockaddr_in dest = {0};
        dest.sin_family = AF_INET;
        dest.sin_port = htons(destPort);
        if (!utils::getHostIP(dest.sin_addr.S_un.S_addr, destIPorHost)) // Write ip address in the right format
        {
            cout << "fail";
            return -1;
        }
        cout << "done" << endl;
    
        ////////////////////////////////////////////////////////////////////////////////////////////////
        //Time to connect to SOCKS5 server
    
        //Creating socket handler
        cout << "Creating socket handler...";
        SOCKET hSocketSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (hSocketSock == INVALID_SOCKET)
        {
            cout << "fail";
            return -1;
        }
        cout << "done" << endl;
    
        cout << "Connecting to socks server...";
        if (connect(hSocketSock, reinterpret_cast<sockaddr *>(&sock), sizeof(sock)) != 0)
        {
            cout << "failed";
            return -1;
        }
        cout << "done" << endl;
    
        //Documentation: https://tools.ietf.org/html/rfc1928
    
        //We have to send first packet which tell to SOCKS5 to enter on
        //sub-negociation mode so we can connect to actual destination server
    
        //    The client connects to the server, and sends a version
        //    identifier/method selection message:
        //    +----+----------+----------+
        //    |VER | NMETHODS | METHODS  |
        //    +----+----------+----------+
        //    | 1  |    1     | 1 to 255 |
        //    +----+----------+----------+
    
        //Allocate space for the first initialize packet and his replay
        char initPacket1[3];        
        initPacket1[0] = 5;  //SOCKS Version. [VER]
        initPacket1[1] = 1;  //No. of methods [NMETHODS]
        initPacket1[2] = 0;  //No auth required [X’00’]
    
        //Now we are sending the packet we just created
        cout << "Sending first init packet...";
        if (sendData(hSocketSock, initPacket1, 3) < 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //And our expected replay format:
        //
        //    The server selects from one of the methods given in METHODS, and
        //    sends a METHOD selection message:
        //    +----+--------+
        //    |VER | METHOD |
        //    +----+--------+
        //    | 1  |   1    |
        //    +----+--------+
    
        //Receiving response from server
    
        char reply1[2];
        cout << "Waiting for response...";
        if (recvData(hSocketSock, reply1, 2) <= 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //reply[0] = our version
        //reply[1] = out method. [X’00’ NO AUTHENTICATION REQUIRED]
        if( !(reply1[0] == 5 && reply1[1] == 0) )
        {
            cout << "Error: bad result on reply:" << (int)reply1[0] << " " << (int)reply1[1] << endl;
            closesocket(hSocketSock);
            return -1;
        }
    
        //We have to build initialize packet. This will transmit to SOCKS5 server
        //the web server we want to connect to.
        //
        //    The SOCKS request is formed as follows:
        //
        //    +----+-----+-------+------+----------+----------+
        //    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
        //    +----+-----+-------+------+----------+----------+
        //    | 1  |  1  | X’00’ |   1  | Variable |    2     |
        //    +----+-----+-------+------+----------+----------+
        //
        //    Where:
        //    o VER protocol version: X’05’
        //    o CMD
        //    o CONNECT X’01’
        //    o BIND X’02’
        //    o UDP ASSOCIATE X’03’
        //    o RSV RESERVED
        //    o ATYP address type of following address
        //    o IP V4 address: X’01’
        //    o DOMAINNAME: X’03’
        //    o IP V6 address: X’04’
        //    o DST.ADDR desired destination address
        //    o DST.PORT desired destination port in network octet
        //    order
    
        //Building that packet
        char initPacket2[10];
        initPacket2[0] = 5; //SOCKS Version;
        initPacket2[1] = 1; //1 = CONNECT, 2 = BIND, 3 = UDP ASSOCIATE;
        initPacket2[2] = 0; //Reserved byte
        initPacket2[3] = 1; //1 = IPv4, 3 = DOMAINNAME, 4 = IPv6
    
        memcpy(&initPacket2[4], &dest.sin_addr.S_un.S_addr, 4);
        memcpy(&initPacket2[8], &dest.sin_port, 2);
    
        //Send the second init packet to server. This will inform the SOCKS5 server
        //what is our target.
        cout << "Sending second init packet...";
        if (sendData(hSocketSock, initPacket2, 10) < 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //Reading the response
        //Expected response format:
    
        //    The SOCKS request information is sent by the client as soon as it has
        //    established a connection to the SOCKS server, and completed the
        //    authentication negotiations. The server evaluates the request, and
        //    returns a reply formed as follows:
    
        //    +----+-----+-------+------+----------+----------+
        //    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        //    +----+-----+-------+------+----------+----------+
        //    | 1  |  1  | X’00’ |  1   | Variable |   2      |
        //    +----+-----+-------+------+----------+----------+
        //                              Where:
        //    o VER protocol version: X’05’
        //    o REP Reply field:
        //        o X’00’ succeeded
        //        o X’01’ general SOCKS server failure
        //        o X’02’ connection not allowed by ruleset
        //        o X’03’ Network unreachable
        //        o X’04’ Host unreachable
        //        o X’05’ Connection refused
        //        o X’06’ TTL expired
        //        o X’07’ Command not supported
        //        o X’08’ Address type not supported
        //        o X’09’ to X’FF’ unassigned
        //    ......................................
    
        char reply2[10];
        cout << "Waiting for response...";
        if (recvData(hSocketSock, reply2, 10) <= 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        cout << "Returned packets: " << endl;
        cout << "VER: " << (int)reply2[0] << endl;
        cout << "REP: " << (int)reply2[1] << endl;
        cout << "RSV: " << (int)reply2[2] << endl;
        cout << "ATY: " << (int)reply2[3] << endl;
        cout << endl;
    
        // use hSocketSock as needed for subsequent traffic...
    
        closesocket(hSocketSock);
        return 0;
    }
    

    另外,您为什么要从主机名中删除 www.?这是实际主机名的一部分,不应删除。这确实会有所不同,因为www.domain.com 可能解析为与domain.com 不同的IP 地址。

    通过让 SOCKS 服务器为您解析主机名,您可以稍微简化应用程序的开销。有时,最终客户端解析的 IP 与代理解析的 IP 不同,具体取决于代理在网络中的位置。由于代理是实际连接到目标主机的那个,它应该是解析IP的那个:

    #include <winsock2.h>
    #include <string>
    #include <iostream>
    
    #include "util.hpp"
    
    using namespace std;
    
    int sendData(SOCKET s, const void *buffer, int buflen)
    {
        const char *pbuf = (const char*) buffer;
        while (buflen > 0)
        {
            int numSent = send(s, pbuf, buflen, 0);
            if (numSent == SOCKET_ERROR)
                return SOCKET_ERROR;
            pbuf += numSent;
            buflen -= numSent;
        }
        return 1;
    }
    
    int recvData(SOCKET s, void *buffer, int buflen)
    {
        char *pbuf = (char*) buffer;
        while (buflen > 0)
        {
            int numRecv = recv(s, pbuf, buflen, 0);
            if (numRecv == SOCKET_ERROR)
                return SOCKET_ERROR;
            if (numRecv == 0)
                return 0;
            pbuf += numRecv;
            buflen -= numRecv;
        }
        return 1;
    }
    
    int main(void)
    {
        //SOCKS5 info
        u_short sockPort = 45554;
        std::string sockIp = "xx.xx.xx.xx";
    
        //Destination info
        u_short destPort = 80;
        std::string destIPorHost = "checkip.dyndns.com";
    
        //Check to see if winsock2 is available
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0)
        {
            cout << "WSA Startup Failed";
            return -1;
        }
    
        if (LOBYTE(wsaData.wVersion) < 2)
        {
            cout << "WSA Version error!";
            return -1;
        }
    
        ///////////////////////////////////////////////////////////////////////////////////////////
        //Init sockaddr for SOCKS5
        cout << "Initialize sock_addr for socks5 connection...";
        sockaddr_in sock;
        sock.sin_family = AF_INET;                      // host byte order
        sock.sin_port = htons(sockPort);                // short, network byte order
        if (!utils::getHostIP(sock.sin_addr.S_un.S_addr, sockIp)) // Write ip address in the right format
        {
            cout << "fail";
            return -1;
        }
        cout << "done" << endl;
    
        ////////////////////////////////////////////////////////////////////////////////////////////////
        //Time to connect to SOCKS5 server
    
        //Creating socket handler
        cout << "Creating socket handler...";
        SOCKET hSocketSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (hSocketSock == INVALID_SOCKET)
        {
            cout << "fail";
            return -1;
        }
        cout << "done" << endl;
    
        cout << "Connecting to socks server...";
        if (connect(hSocketSock, reinterpret_cast<sockaddr *>(&sock), sizeof(sock)) != 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //Documentation: https://tools.ietf.org/html/rfc1928
    
        //We have to send first packet which tell to SOCKS5 to enter on
        //sub-negociation mode so we can connect to actual destination server
    
        //    The client connects to the server, and sends a version
        //    identifier/method selection message:
        //    +----+----------+----------+
        //    |VER | NMETHODS | METHODS  |
        //    +----+----------+----------+
        //    | 1  |    1     | 1 to 255 |
        //    +----+----------+----------+
    
        //Allocate space for the first initialize packet and his replay
        char initPacket1[3];        
        initPacket1[0] = 5;  //SOCKS Version. [VER]
        initPacket1[1] = 1;  //No. of methods [NMETHODS]
        initPacket1[2] = 0;  //No auth required [X’00’]
    
        //Now we are sending the packet we just created
        cout << "Sending first init packet...";
        if (sendData(hSocketSock, initPacket1, 3) < 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //And our expected replay format:
        //
        //    The server selects from one of the methods given in METHODS, and
        //    sends a METHOD selection message:
        //    +----+--------+
        //    |VER | METHOD |
        //    +----+--------+
        //    | 1  |   1    |
        //    +----+--------+
    
        //Receiving response from server
    
        char reply1[2];
        cout << "Waiting for response...";
        if (recvData(hSocketSock, reply1, 2) <= 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        //reply[0] = our version
        //reply[1] = out method. [X’00’ NO AUTHENTICATION REQUIRED]
        if( !(reply1[0] == 5 && reply1[1] == 0) )
        {
            cout << "Error: bad result on reply:" << (int)reply1[0] << " " << (int)reply1[1] << endl;
            closesocket(hSocketSock);
            return -1;
        }
    
        //We have to build initialize packet. This will transmit to SOCKS5 server
        //the web server we want to connect to.
        //
        //    The SOCKS request is formed as follows:
        //
        //    +----+-----+-------+------+----------+----------+
        //    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
        //    +----+-----+-------+------+----------+----------+
        //    | 1  |  1  | X’00’ |   1  | Variable |    2     |
        //    +----+-----+-------+------+----------+----------+
        //
        //    Where:
        //    o VER protocol version: X’05’
        //    o CMD
        //    o CONNECT X’01’
        //    o BIND X’02’
        //    o UDP ASSOCIATE X’03’
        //    o RSV RESERVED
        //    o ATYP address type of following address
        //    o IP V4 address: X’01’
        //    o DOMAINNAME: X’03’
        //    o IP V6 address: X’04’
        //    o DST.ADDR desired destination address
        //    o DST.PORT desired destination port in network octet
        //    order
    
        int hostlen = max(destIPorHost.size(), 255);
    
        //Building that packet
        char *initPacket2 = new char[7+hostlen];
        initPacket2[0] = 5; //SOCKS Version;
        initPacket2[1] = 1; //1 = CONNECT, 2 = BIND, 3 = UDP ASSOCIATE;
        initPacket2[2] = 0; //Reserved byte
        initPacket2[3] = 3; //1 = IPv4, 3 = DOMAINNAME, 4 = IPv6
        initPacket2[4] = (char) hostlen;
        memcpy(&initPacket2[5], destIPorHost.c_str(), hostlen);
        *((u_short*) &(initPacket2[5+hostlen])) = htons(destPort);
    
        //Send the second init packet to server. This will inform the SOCKS5 server
        //what is our target.
        cout << "Sending second init packet...";
        if (sendData(hSocketSock, initPacket2, 7+hostlen) < 0)
        {
            cout << "failed";
            delete[] initPacket2;
            closesocket(hSocketSock);
            return -1;
        }
        cout << "done" << endl;
    
        delete[] initPacket2;
    
        //Reading the response
        //Expected response format:
    
        //    The SOCKS request information is sent by the client as soon as it has
        //    established a connection to the SOCKS server, and completed the
        //    authentication negotiations. The server evaluates the request, and
        //    returns a reply formed as follows:
    
        //    +----+-----+-------+------+----------+----------+
        //    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        //    +----+-----+-------+------+----------+----------+
        //    | 1  |  1  | X’00’ |  1   | Variable |   2      |
        //    +----+-----+-------+------+----------+----------+
        //                              Where:
        //    o VER protocol version: X’05’
        //    o REP Reply field:
        //        o X’00’ succeeded
        //        o X’01’ general SOCKS server failure
        //        o X’02’ connection not allowed by ruleset
        //        o X’03’ Network unreachable
        //        o X’04’ Host unreachable
        //        o X’05’ Connection refused
        //        o X’06’ TTL expired
        //        o X’07’ Command not supported
        //        o X’08’ Address type not supported
        //        o X’09’ to X’FF’ unassigned
        //    ......................................
    
        char reply2[262];
        cout << "Waiting for response...";
        if (recvData(hSocketSock, reply2, 4) <= 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
    
        if (!(reply2[0] == 5 && reply2[1] == 0))
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
    
        int replylen = 4;
        switch (reply2[3])
        {
            case 1:
            {
                if (recvData(hSocketSock, &reply2[replylen], 4) <= 0)
                {
                    cout << "failed";
                    closesocket(hSocketSock);
                    return -1;
                }
                replylen += 4;
                break;
            }
    
            case 3:
            {
                if (recvData(hSocketSock, &reply2[replylen], 1) <= 0)
                {
                    cout << "failed";
                    closesocket(hSocketSock);
                    return -1;
                }
    
                hostlen = reply2[replylen];
                ++replylen;
    
                if (recvData(hSocketSock, &reply2[replylen], hostlen) <= 0)
                {
                    cout << "failed";
                    closesocket(hSocketSock);
                    return -1;
                }
    
                replylen += hostlen;
                break;
            }
    
            case 4:
            {
                if (recvData(hSocketSock, &reply2[replylen], 16) <= 0)
                {
                    cout << "failed";
                    closesocket(hSocketSock);
                    return -1;
                }
                replylen += 16;
                break;
            }
        }
    
        if (recvData(hSocketSock, &reply2[replylen], 2) <= 0)
        {
            cout << "failed";
            closesocket(hSocketSock);
            return -1;
        }
    
        cout << "done" << endl;
    
        // use hSocketSock as needed for subsequent traffic...
    
        closesocket(hSocketSock);
        return 0;
    }
    

    【讨论】:

    • 感谢您的代码,它有效!但是,它仍然无法像以前一样接收到第一个数据包发送的应答。请告诉我,您是如何(从 RFC)弄清楚如何构建第二个数据包的?您已经添加了带有主机长度的第 4 个字节。 RFC 只是没有指定这一点。我问你这个是因为第一个数据包也可能是错误的,这就是它有时仍然失败的原因。
    • @curiosul 第一个数据包没问题。如果您仍然无法收到回复,那么您可能没有将其发送到有效的 SOCKS v5 代理,或者该代理需要身份验证。至于额外的主机长度字节,请仔细阅读 RFC 的第 5 节:“X'03' 地址字段包含一个完全限定的域名。地址字段的第一个八位字节包含的数量后跟的名称八位字节,没有终止 NUL 八位字节。"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-27
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多