通过 SOCKS5 c++ 发送和接收

Posted

技术标签:

【中文标题】通过 SOCKS5 c++ 发送和接收【英文标题】:Send and receive via SOCKS5 c++ 【发布时间】:2013-08-09 15:41:01 【问题描述】:

我正在使用 SOCKS5 代理 (TOR)。我能够建立连接,但现在我不知道如何向/从目的地发送和接收数据。感谢帮助。代码:

#include <stdio.h>
#include <WinSock2.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define PUT_BYTE(ptr,data) (*(unsigned char*)ptr = data)

int main()

    WORD wVersionRequested = MAKEWORD(2,0);
    WSADATA wsaData;
    if(WSAStartup(wVersionRequested,&wsaData) != 0 )
    
        return 1;
    
    int fd = socket( AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
        return 1;
    struct sockaddr_in destaddr;
    destaddr.sin_addr.s_addr = inet_addr("xx.xx.xx.xx");
    int dest_port = 80;

    struct sockaddr_in saddr;
    saddr.sin_port = htons(9150);
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int rv = connect( fd, (struct sockaddr *)&saddr, sizeof(saddr));
    if(rv < SOCKET_ERROR)
        return 1;
    char buf[256], *ptr;
    ptr = buf;
    PUT_BYTE( ptr++,5);
    PUT_BYTE( ptr++,1);
    PUT_BYTE(ptr++,0x00);
    send(fd,buf,ptr-buf,0);
    recv(fd,buf,2,0);
    if ( (buf[0] != 5) || buf[1] == 0xFF )
    
        return 1;
    
    ptr = buf;
    PUT_BYTE(ptr++,5);
    PUT_BYTE(ptr++,1);
    PUT_BYTE(ptr++,0);
    PUT_BYTE(ptr++,1);
    memcpy( ptr, &destaddr.sin_addr.s_addr,sizeof(destaddr.sin_addr));
    ptr += sizeof(destaddr.sin_addr);
    PUT_BYTE(ptr++,dest_port>>8);
    PUT_BYTE(ptr++,dest_port&0xFF);
    send(fd,buf,ptr-buf,0);
    recv(fd,buf,4,0);
    if(buf[1] != 0x00)
    
        return 1;
    
    ptr = buf + 4;
    switch ( buf[3] )                          
    case 1:                                     
        recv( fd, ptr, 4+2,0 );              
        break;
    case 3:                                     
        recv( fd, ptr, 1 ,0);                 
        recv( fd, ptr+1, *(unsigned char*)ptr + 2,0);
        break;
    case 4:                                    
        recv( fd, ptr, 16+2,0 );              
        break;
    

    printf("Succes!");
    //How to send and receive data now? Now we are connected on port 80 and for example I want to send http get request and receive the answer.

    return 0;



现在如何发送和接收数据?现在我们连接在 80 端口上,例如我想发送 http get 请求并接收答案。

【问题讨论】:

【参考方案1】:

一旦您成功通过代理进行身份验证并告诉它连接到哪里,然后您只需使用与代理的现有连接发送/接收您想要的数据(在本例中为 HTTP 数据),就像您已经连接一样直接到目标服务器而不是代理。建立代理会话后,所有后续发送/接收对您的应用都是透明的。

更新:您可能还想清理代码,使其更易于阅读,修复现有的错误处理,并添加一些缺少的额外错误处理:

#include <stdio.h>
#include <WinSock2.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")

#include <pshpack1.h>
struct socks5_ident_req

    unsigned char Version;
    unsigned char NumberOfMethods;
    unsigned char Methods[256];
;

struct socks5_ident_resp

    unsigned char Version;
    unsigned char Method;
;

struct socks5_req

    unsigned char Version;
    unsigned char Cmd;
    unsigned char Reserved;
    unsigned char AddrType;
    union 
        in_addr IPv4;
        in6_addr IPv6;
        struct 
            unsigned char DomainLen;
            char Domain[256];
        ;
     DestAddr;
    unsigned short DestPort;
;

struct socks5_resp

    unsigned char Version;
    unsigned char Reply;
    unsigned char Reserved;
    unsigned char AddrType;
    union 
        in_addr IPv4;
        in6_addr IPv6;
        struct 
            unsigned char DomainLen;
            char Domain[256];
        ;
     BindAddr;
    unsigned short BindPort;
;
#include <poppack.h>

bool sendData(SOCKET fd, void *data, int len)

    char *ptr = (char *) data;

    while (len > 0)
    
        int sent = send(fd, ptr, len, 0);
        if (sent <= 0)
        
            printf("send() error: %d", WSAGetLastError());
            return false;
        
        ptr += sent;
        len -= sent;
    

    return true;


int recvData(SOCKET fd, void *data, int len, bool disconnectOk = false)

    char *ptr = (char *) data;
    int total = 0;

    while (len > 0)
    
        int recvd = recv(fd, ptr, len, 0);
        if (recvd < 0)
        
            printf("recv() error: %d", WSAGetLastError());
            return -1;
        
        if (recvd == 0)
        
            if (disconnectOk)
                break;
            printf("disconnected");
            return -1;
        
        ptr += recvd;
        len -= recvd;
        total -= recvd;
    

    return total;


bool socksLogin(SOCKET fd)

    socks5_ident_req req;
    socks5_ident_req resp;

    req.Version = 5;
    req.NumberOfMethods = 1;
    req.Methods[0] = 0x00;
    // add other methods as needed...

    if (!sendData(fd, &req, 2+req.NumberOfMethods))
        return false;

    if (recvData(fd, &resp, sizeof(resp)) == -1)
        return false;

    if (resp.Version != 5)
    
        printf("SOCKS v5 identification failed");
        return false;
    

    if (resp.Method == 0xFF)
    
        printf("SOCKS v5 authentication failed");
        return false;
    

    /*
    if (resp.Method != 0x00)
    
        // authenticate as needed...
    
    */

    return true;


bool socksRequest(SOCKET fd, const socks5_req &req, socks5_resp &resp)

    memset(&resp, 0, sizeof(resp));

    if (!sendData(fd, &req, 4))
        return false;

    switch (req.AddrType)
                             
        case 1:                                     
        
            if (!sendData(fd, &(req.DestAddr.IPv4), sizeof(in_addr)))
                return false;              

            break;
        
        case 3:
                                             
            if (!sendData(fd, &(req.DestAddr.DomainLen), 1))
                return false;

            if (!sendData(fd, req.DestAddr.Domain, req.DestAddr.DomainLen))
                return false;

            break;
        
        case 4:
                                            
            if (!sendData(fd, &(req.DestAddr.IPv6), sizeof(in6_addr)))
                return false;              

            break;
        

        default:
        
            printf("SOCKS 5 requesting unknown address type");
            return false;
        
    

    unsigned short port = htons(req.DestPort);
    if (!sendData(fd, &port, 2))
        return false;

    if (recvData(fd, &resp, 4) == -1)
        return false;

    switch (resp.AddrType)
                             
        case 1:                                     
        
            if (recvData(fd, &(resp.BindAddr.IPv4), sizeof(in_addr)) == -1)
                return false;              

            break;
        
        case 3:
                                             
            if (recvData(fd, &(resp.BindAddr.DomainLen), 1) == -1)
                return false;

            if (recvData(fd, resp.BindAddr.Domain, resp.BindAddr.DomainLen) == -1)
                return false;

            break;
        
        case 4:
                                            
            if (recvData(fd, &(resp.BindAddr.IPv6), sizeof(in6_addr)) == -1)
                return false;              

            break;
        

        default:
        
            printf("SOCKS 5 bound to unknown address type");
            return false;
        
    

    if (recvData(fd, &port, 2, 0) == -1)
        return false;

    resp.BindPort = ntohs(port);

    return true;


bool socksConnect(SOCKET fd, const in_addr &dest, unsigned short port)

    socks5_req req;
    socks5_resp resp;

    req.Version = 5;
    req.Cmd = 1;
    req.Reserved = 0;
    req.AddrType = 1;
    req.DestAddr.IPv4 = dest;
    req.DestPort = port;

    if (!socksRequest(fd, req, resp))
        return false;

    if (resp.Reply != 0x00)
    
        printf("SOCKS v5 connect failed, error: 0x%02X", resp.Reply);
        return false;
    

    return true;


int main()

    WSADATA wsaData;
    int rv = WSAStartup(MAKEWORD(2,0), &wsaData);
    if (rv != 0)
    
        printf("WSAStartup() error: %d", rv);
        return 1;
    

    SOCKET fd = socket( AF_INET, SOCK_STREAM, 0);
    if (fd == INVALID_SOCKET)
    
        printf("socket() error: %d", WSAGetLastError());
        return 1;
    

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    saddr.sin_port = htons(9150);

    if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) != 0)
    
        printf("connect() error: %d", WSAGetLastError());
        return 1;
    

    if (!socksLogin(fd))
        return 1;

    if (!socksConnect(fd, inet_addr("xx.xx.xx.xx"), 80))
        return 1;

    printf("Success!");

    // now send/receive desired data as needed using existing fd ...

    return 0;

【讨论】:

我认为这比在下面使用 curl 更简单。 查看时间戳。我的答案是在 curl 答案发布前一个多月发布的。【参考方案2】:

我最近发现了一个现代 C++ 实现,用于通过 SOCKS5 代理服务器发送和接收数据,这个人看起来也实现了 SOCKS5 的用户/通过身份验证(以及匿名模式),而且具有远程和本地 DNS 解析选项(以获得更多隐私)。

看一看:https://github.com/harsathAI/SOCKS5-Proxy-Handler

(注意:我不是上面链接的那个 SOCKS5 客户端界面的作者)

【讨论】:

【参考方案3】:

Olof,您使用的例程非常困难。如果您的目标是让某些东西真正发挥作用,我建议您使用构建在这些低级例程之上的工具。

最好的是卷曲。在 curl 网站上,他们将自己与您可以考虑的其他工具进行比较:http://curl.haxx.se/docs/comparison-table.html

编辑:好的,所以你否决了我的回答。去看看 torsocks 的源代码,它试图使用这些例程。编译它并尝试运行它。它有效吗?不,看看源代码。尝试运行测试套件。它有效吗?不。看看他们调用的例程。许多已被弃用。你能弄清楚哪些例程已被弃用吗?

如果您环顾四周,您会发现能够通过 Tor 实际传输数据的人正在使用 curl。

【讨论】:

认真的吗?你想让他从 C++ 程序中调用curl Curl 有许多编程语言的库,包括 C++。当 curl 为您完成所有这些操作时,尝试使用 send/recv 是愚蠢的。无需重新发明***。 我以为您指的是“卷曲”工具。无论如何,仅仅因为你不能编译一个 SOCKS 客户端并不意味着我们其他人不能。事实上,它工作得很好。 Curl 适用于脚本,而不适用于程序。 我仍然无法相信,即使在 5 年后,有人会说“curl 库适合脚本,而不是程序”。互联网本身只对脚本有好处,所以我想这很好,考虑到它对 C++ 来说有点太慢了。

以上是关于通过 SOCKS5 c++ 发送和接收的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SOCKS5 需要通过 UDP 中继 UDP?

C++ Socks5 通信问题

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

推送:啥是socks5代理ip

使用 C++ 通过 TCP 发送文件,接收错误的大小

尝试通过 Process Pipe 发送/接收到 python 包装器,C++ 代码