简体   繁体   中英

Download HTTP Website through SOCKS5 using Winsock2

As title say, I want to download a website source through a SOCKS5 proxy server using Winsock2. The program is compiled with MinGW.

The documentation used was from RFC 1928 .

To introduce you in this subject, in order to connect to establish a connection between SOCKS5 server and webserver I have to send two requests to socks server. If those two are successfully, then I can use the socket handle with send() and recv() to interact with webserver through tunnel we have just created.

According to RFC, first packet will looks like:

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

I want only one method, the one without auth. And I have build the packet this way:

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;
}

The response should be a 2 byte packet with the version and method. First problem is that sporadically this step fail. More specifically recv() function works but bytes received are not valid. But it only happens some times.

Next, I have to build the second packet which tells proxy server to connect to destination website. The packet I have to build have the following form:

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

And the way I build that packet:

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;
}

The second big problem is that the response field is every time "07" which means "Command not supported".

I have already to modify second byte from second packet but with no success.

Most probably I miss something when build the packet and I can't figure out what.

Full program: 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;
}

Byte REP returned is 07 but I need an 0 in order to continue.

I have attached a source files with helpfull functions in case you want to test.

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;
}

Your calculations of initPacket1Length and initPacket2Length are wrong, so they are both getting set to 4 instead of 3 and 10, respectively. This is because you are passing char* pointers to sizeof instead of char[] buffers, like you are expecting ( sizeof() of any pointer type is 4 on a 32bit system, and 8 on a 64bit system).

This means the first packet is sending 4 bytes instead of 3, but you did not allocate 4 bytes, so you are sending garbage in the 4th byte (you are lucky your code did not just crash altogether). Since the SOCKS server is expecting only 3 bytes initially, the 4th byte and subsequent 4 bytes get interpreted as a malformed 5-byte command that fails.

Since you are using hard-coded sizes when allocating the buffers, you should just hard-code the lengths to match.

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

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

Also, when memset() ing a fixed buffer, using strlen() to calculate the size is just plain wrong. You should be using sizeof instead.

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

You are also ignoring the return values of send() and recv() . They return the number of bytes actually sent/received. But you are only checking the return values for failure, not success. TCP is a streaming transport, send() and recv() can send/receive fewer bytes than requested, so you have to account for that.

That being said, try something more like this instead:

#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;
}

Also, why are you removing www. from hostnames? That is part of a actual hostname and should not be removed. It does make a difference, as www.domain.com may resolve to a different IP address than just domain.com .

You can simplify your app's overhead a little by having the SOCKS server resolve the hostname for you. Sometimes an IP resolved by an end client is different than the IP resolved by a proxy, depending on where the proxy is in the network. Since the proxy is the one making the actual connection to the target host, it should be the one to resolve the 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM