通过 SOCKS5 代理的 SSL 连接

Posted

技术标签:

【中文标题】通过 SOCKS5 代理的 SSL 连接【英文标题】:SSL connection via SOCKS5 proxy 【发布时间】:2019-03-30 19:41:29 【问题描述】:

我正在尝试编写 C 代码以通过 SOCKS5 代理服务器打开与远程服务器的 SSL 连接。

我已成功打开与 SOCKS5 代理服务器的 tcp 连接。但是,我不知道如何与远程服务器(不同于代理服务器)建立 SSL 连接。

// p->ai_addr has the SOCKS5 proxy address
m_sock_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
connect(m_sock_fd, p->ai_addr, p->ai_addrlen);

char socks5_request[256] = 0, response[256] = 0;
sprintf(socks5_request, "\5\1\0");
send(m_sock_fd, socks5_request, 4, 0);
recv(m_sock_fd, response, 256, 0);

assert(response[0] == '\5' && response[1] == '\0'); // OK

m_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
m_ssl = SSL_new(m_ssl_ctx);
SSL_set_fd(m_ssl, m_sock_fd);

SSL_connect(m_ssl); // returns -1

SSL_connect 失败并给出错误:1408F10B:SSL 例程:ssl3_get_record:错误的版本号:ssl/record/ssl3_record.c:332:

我有两个问题。

    为什么会出现此错误? 进行 SSL 连接时,我在哪里指定远程 IP 地址?

2019 年 4 月 2 日更新

以下代码用于向代理服务器发送连接请求。对 CheckConnectivity() 的最后一次调用发现连接已关闭。

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("*.*.*.*");
addr.sin_port = htons(443);

SOCKSRequest connect_request;

// 5: protocol number, 1: connect, 0: reserved, 1: ipv4
connect_request.m_version = '\5';
connect_request.m_type = '\1';
connect_request.m_reserved = '\0';
connect_request.m_addr_type = '\1';

connect_request.m_ip = addr.sin_addr.s_addr;
connect_request.m_port = addr.sin_port;

CheckConnectivitiy();

if (!send(m_sock_fd, &connect_request, 4, 0))
    return 0;

CheckConnectivitiy();

if (!send(m_sock_fd, &connect_request.m_ip, sizeof(in_addr), 0))
    return 0;

CheckConnectivitiy();

if (!send(m_sock_fd, &connect_request.m_port, 2, 0))
    return 0;

CheckConnectivitiy();  // connection was closed by peer here!

CheckConnectivity 的实现。

int CheckConnectivitiy() const

    int error = 0;
    socklen_t len = sizeof (error);
    int retval = getsockopt (m_sock_fd, SOL_SOCKET, SO_ERROR, &error, &len);

    if (retval != 0) 
    
        /* there was a problem getting the error code */
        fprintf(stderr, "error getting socket error code: %s\n", strerror(retval));
        return 0;
    

    if (error != 0) 
    
        /* socket has a non zero error status */
        fprintf(stderr, "socket error: %s\n", strerror(error));
        return 0;
    

    return 1;

【问题讨论】:

【参考方案1】:

您只需要 1 个连接,即您到代理。你告诉代理它接下来应该连接到哪里(你没有这样做!)。只有代理成功连接,您才能与目标服务器建立有效连接。只有这样您才能发送 SSL/TLS 握手,就像您直接连接到目标服务器一样。

我建议您阅读RFC 1928 了解 SOCKS v5 协议。在尝试使用 SSL/TLS 之前,您正在发送身份验证请求并读取其响应,但您没有发送 Connect 请求并且读取的是响应。这就是握手失败的原因。您尚未连接到目标服务器。

【讨论】:

非常感谢您的解释!我现在,在身份验证后,发送一个连接请求,回复看起来不错(REPLY == x'00')。但是,我在调用 SSL_connect(m_ssl) 时得到了 SIGPIPE。看起来远程服务器已关闭连接。调查此问题的最佳方法是什么? 我发送连接请求后,远程服务器似乎关闭了连接。 @chukunu 没有足够的信息来诊断。代理关闭是连接到您,还是远程服务器关闭是连接到代理?如果是层,它是在代理连接后立即执行,还是在您开始发送 SSL/TLS 握手之后执行? 在每次操作后添加代码检查连接后,我发现在发送连接请求后连接立即关闭(请参阅更新的问题)。所以关闭与 SSL/TLS 无关。仍然不确定代理或远程是否关闭了连接。了解它的最佳方式是什么? @chukunu 您需要在代理的两端运行数据包嗅探器以查看实际情况。但是您如何检查连接性?您可以做到这一点的唯一真正方法是从套接字读取,并且您不应该在发送命令字节的过程中这样做。此外,send() 返回发送的字节数,或错误时返回 -1,当缓冲区长度不为 0 时,它将从不返回 0。您需要更改您的 if (!send(...)) 以正确处理错误.

以上是关于通过 SOCKS5 代理的 SSL 连接的主要内容,如果未能解决你的问题,请参考以下文章

使用 SSLSocket 的 SOCKS5 代理

通过身份验证连接并保持与 socks5 代理服务器的连接

通过命令行通过 SOCKS5 代理打开 putty ssh 连接

虚拟机中使用本机的socks5代理

ew通过socks5一层代理-访问内网二层网络-实战篇

ew通过socks5一层代理-访问内网二层网络-实战篇