使用Winsock2通过SOCKS5下载HTTP网站

咖啡因

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

使用的文档来自RFC 1928

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

根据RFC,第一个数据包将如下所示:

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

我只需要一种方法,一种不需要auth的方法。我已经用这种方式构建了数据包:

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”,这意味着“命令不支持”。

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

构建数据包时,我很可能会错过一些东西,但我不知道该怎么办。

完整程序:main.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;
}
雷米·勒博(Remy Lebeau)

您对initPacket1Length和的计算initPacket2Length是错误的,因此它们都分别设置为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可能解析为与IP地址不同的IP地址domain.com

通过让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;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

WinSock2 HTTP GET使用

来自分类Dev

如何使用Python 2.7通过HTTP使用多线程下载文件(异步下载)

来自分类Dev

HTML5下载网站为PDF

来自分类Dev

使用Python通过Tor(SOCKS5)发送UDP请求

来自分类Dev

angular2通过整个网站传递数据

来自分类Dev

使用ASP.Net WebAPI 2通过HTTP传输原始字节

来自分类Dev

使用ASP.Net WebAPI 2通过HTTP传输原始字节

来自分类Dev

无法使用urllib2下载整个文件

来自分类Dev

使用urllib2下载zipfile失败

来自分类Dev

使用Struts2下载任何内容类型

来自分类Dev

如何使用Struts 2下载语言环境文件

来自分类Dev

使用OAuth2下载Javascript中的文件

来自分类Dev

使用Qt5通过WebSocket发送Json

来自分类Dev

Angular 2-8通过使用材质在没有formbuilder的情况下显示错误的方法

来自分类Dev

如何在Windows上使用freeSSHd设置HTTP / SOCKS5隧道代理?

来自分类Dev

用k6下载整个网站

来自分类Dev

我如何使用python 3通过基本身份验证下载Teamcity工件

来自分类Dev

如何通过angularJS和webaAPI2下载内存流对象

来自分类Dev

通过SOCKS5代理进行SSL连接

来自分类Dev

如何通过VPN或Socks5绕过代理

来自分类Dev

Firejail无法通过socks5 SSH隧道工作

来自分类Dev

将HTTP请求转换为SOCKS5

来自分类Dev

WINSOCK2 WSAAsyncSelect不推荐使用的问题

来自分类Dev

+2通过归纳证明

来自分类Dev

FFMPeg 2通过C#

来自分类Dev

Winsock2监听循环

来自分类Dev

404通过IP地址从Internet访问SharePoint网站

来自分类Dev

通过php从s3下载大文件

来自分类Dev

ASP.NET MVC5通过传递参数从jQuery启动动态文件下载