【问题标题】:Authentication - SOCKS 5身份验证 - 袜子 5
【发布时间】:2018-02-27 04:12:22
【问题描述】:

我正在编写一个与 SOCKS 5 客户端(maxthon 浏览器)通信的 SOCKS 5 服务器。我让它在没有身份验证的情况下工作。但是,当我添加身份验证时,

客户端向服务器发送用户/密码 服务器验证用户/通行证

服务器向客户端发送响应

+----+--------+
|VER | STATUS |
+----+--------+
| 1  |   1    |
+----+--------+ 

VER = 0x01 STATUS = 0x00(成功)

  1. 我可以假设这是身份验证的结束吗?

之后我尝试从客户端读取数据,

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

但是此时读取的字节不等于该结构的大小所期望的字节。读取的字节数 == 0。

  1. 有什么问题?

如果我取消身份验证,我的服务器就可以正常工作。但是我想让它与它一起工作。

编辑

这是服务器验证客户端后的代码:

    socks5_login SOCKS5_Login;
    if (Str::Compare(UserName, PUCHAR("test")) == 0 && Str::Compare(Password, PUCHAR("test")) == 0) {

        SOCKS5_Login.Version = 0x01;
        SOCKS5_Login.Status  = 0x00;
        if (Http::SendProxyData(Settings->Sock, PCHAR(&SOCKS5_Login), sizeof(socks5_login), Settings->Func) != TRUE)
            return FALSE;

        UCHAR Status;
        Settings->Func.Recv(Settings->Sock, PCHAR(Status), sizeof(UCHAR), 0);

        if (Status != NO_ERROR)
            return FALSE;

        /*
            The SOCKS request is formed as follows:
                +----+-----+-------+------+----------+----------+
                |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
                +----+-----+-------+------+----------+----------+
                | 1  |  1  | X'00' |  1   | Variable |    2     |
                +----+-----+-------+------+----------+----------+
        */

        socks5_request Request;
        if (Http::ReadProxyData(Settings->Sock, PCHAR(&Request.SOCKS5_Header), sizeof(socks5_header), Settings->Func) != TRUE)
            return FALSE;

        if (Request.SOCKS5_Header.Command != 1/*CONNECT*/ && 
            Request.SOCKS5_Header.AddressType != 1 /*IPv4*/ &&
            Request.SOCKS5_Header.Version != 5 /*SOCKS Version 5*/)
            return FALSE;

        // ...

读取Request.SOCKS5_Header时,Http::ReadProxyData()函数返回FALSE(recv()返回0)。

【问题讨论】:

  • 这就是 RFC1929 的工作原理,是的。您确定客户对您的回复满意吗?
  • @tadman 如果是这样的话,为什么在向客户端发送身份验证响应后我无法接收到结构?
  • 您可能没有正确发送响应,而客户端正在等待某些事情发生,然后才说出其他内容。你确定数据已经从套接字中“刷新”了吗?
  • 你说的“脸红”是什么意思?
  • "读取的字节数 == 0" - 如果 recv() 返回 0,则表示对端正常断开连接。这可能意味着您没有正确发回您的响应,因此客户端最终失败并关闭连接。请显示您的实际代码,您可能做错了什么。

标签: c++ winapi socks


【解决方案1】:

您在发送身份验证回复后正在阅读UCHAR(并且您正在将其未定义值类型转换为PCHAR,因为您没有使用@将其传递给Recv() 987654327@ 地址运算符。所以你很可能崩溃你的代码!)。客户不会回复您的回复。通过读取UCHAR,您正在读取下一个请求的VER 字段,这意味着之后读取完整的Request 可能会失败。

您根本不应该阅读UCHAR

更重要的是,您将身份验证回复的VER 字段设置为0x01,而不是0x05。您说为下一个请求读取的字节数为 0。这意味着客户端正在优雅地关闭其端的连接,如果您发送回格式错误的身份验证回复,这是有道理的。

更仔细地重新阅读RFC 1928RFC 1929。图表显示每个字段的字节大小,而不是您应该将字段设置为的(这些值在图表后面的列表中描述)。例如:

+----+--------+
|VER | STATUS |
+----+--------+
| 1  |   1    |
+----+--------+ 

表示VER 字段大小为 1 个字节,STATUS 大小为 1 个字节。并不是说您应该将 VER 字段设置为 1。

此外,您显示的代码末尾的if 应该使用|| 运算符而不是&& 运算符。不过,您真的不应该以您的方式验证请求字段,因为不同的条件要求您将不同的错误代码发送回客户端,以便它知道请求失败的原因。

试试这样的:

socks5_login LoginReply;
LoginReply.Version = 5;

if (Str::Compare(UserName, PUCHAR("test")) == 0 && Str::Compare(Password, PUCHAR("test")) == 0)
    LoginReply.Status = 0; /*success*/
else
    LoginReply.Status = 1; /*failure*/

if (Http::SendProxyData(Settings->Sock, PCHAR(&LoginReply), sizeof(LoginReply), Settings->Func) != TRUE)
{
    // close the connection...
    return FALSE;
}

if (LoginReply.Status != 0)
{
    // close the connection...
    return FALSE;
}

/*
The SOCKS request is formed as follows:
    +----+-----+-------+------+----------+----------+
    |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+
*/

socks5_request Request;

// I'm assuming that SOCKS5_Header is only the 1st 4 bytes of the request...
if (Http::ReadProxyData(Settings->Sock, PCHAR(&Request.SOCKS5_Header), sizeof(socks5_header), Settings->Func) != TRUE)
{
    // close the connection...
    return FALSE;
}

if (Request.SOCKS5_Header.Version != 5) /*SOCKS Version 5*/
{
    // I'm assuming you have something like this defined.
    // Adjust this code as needed. What is important is that
    // you need to send back a Reply code before disconnecting...
    socks5_reply Reply;
    UCHAR dst[6] = {0}; // <-- if socks5_reply doesn't have DST.ADDR/DST.PORT fields of its own...

    Reply.SOCKS5_Header.Version = 5;
    Reply.SOCKS5_Header.Status = 1; /*general SOCKS server failure*/
    Reply.SOCKS5_Header.Reserved = 0;
    Reply.SOCKS5_Header.AddressType = 1;

    Http::SendProxyData(Settings->Sock, PCHAR(&Reply.SOCKS5_Header), sizeof(socks5_header), Settings->Func);
    Http::SendProxyData(Settings->Sock, PCHAR(dst), sizeof(dst), Settings->Func);

    // close the connection...
    return FALSE;
}

if (Request.SOCKS5_Header.Command != 1) /*CONNECT*/
{
    // same as above...
    Reply.SOCKS5_Header.Status = 7; /*Command not supported*/
    ...
    return FALSE;
}

if (Request.SOCKS5_Header.AddressType != 1) /*IPv4*/
{
    // same as above...
    Reply.SOCKS5_Header.Status = 8; /*Address type not supported*/
    ...
    return FALSE;
}

// finish reading DST.ADDR and DST.PORT fields into Request...
// use Request as needed...
// send a Reply accordingly...

【讨论】:

  • 我试图将你的代码提交到我的。但是,它仍然在此 recv PCHAR(&amp;Request.SOCKS5_Header) 上失败。
  • 很难回答,如果您还没有展示socks5_loginsocks5_request 实际声明为什么,或者Http::SendProxyData()Http::ReadProxyData() 是如何实际实现的。当您不提供minimal reproducible example 时,您希望人们如何帮助您?在任何情况下,我建议您使用像 Wireshark 这样的数据包嗅探器来查看实际的 SOCKS 流量,并确保网络上的所有内容都正确格式化。很有可能,事实并非如此。
猜你喜欢
  • 1970-01-01
  • 2013-09-30
  • 2015-07-02
  • 1970-01-01
  • 1970-01-01
  • 2015-04-25
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
相关资源
最近更新 更多