假期的时候把socks5代理的RFC全部读完了,有些体会不敢独享,在这里写出来大家一起评论评论,如有错误敬请提出。
下面假设使用TCP连接方式。首先需要和代理服务器之间建立连接,这里没什么复杂的,简单的connect(serverIP, serverPort)就可以了。连接成功之后,需要使用send()发送命令字,以便确定是否需要验证,下面是RFC里面的命令字格式:
项目 | 版本 | 方式数目 | 连接方式 | … |
项目长度 | 1 | 1 | 1-255 | … |
首先"版本"这一项固定是 X"05"(socks version 5),方式数目告诉server究竟提交了几种连接方式的请求,至于连接方式则可以有多个。下面就是方式列表:
连接方式 | 含义 |
X’00’ | 无需验证,直接继续 |
X’01’ | GSSAPI |
X’02’ | 需要用户名/密码 |
X’03’ to X’7F’ | IANA ASSIGNED |
X’80’ to X’FE’ | 保留方式,可以自己灵活选用 |
X’FF’ | 未包含符合要求的方式 |
接下来是server的回应:
项目 | 版本 | 允许的连接方式 |
项目长度 | 1 | 1 |
版本不必说,仍然固定是 X"05",允许的连接方式则是在你提交的众多连接方式中,由server选出一个可以接受的,然后返回来;如果没有,那么返回就是 X"FF"。其中一般用到的就是 X"00"和 X"02"了。它们之间的区别就在于 X"02"方式需要发送用户名/密码,验证通过后的过程则和 X"00"方式没有任何区别。
客户端识别到server返回 X"02"之后,发送下列格式验证字串:
项目 | VER | 用户名长度 | 用户名 | 密码长度 | 密码 |
项目长度 | 1 | 1 | 1-255 | 1 | 1-255 |
注意:这里的VER有别于上边,固定是 X"01"。用户名/密码最大长度是255。
server端验证完毕后返回结果:
项目 | VER | 验证结果 |
项目长度 | 1 | 1 |
验证结果是 X"00"的话,就表示验证通过,否则都是不过…
接下来的过程一样,就是发送请求命令字了:
项目 | 版本 | 命令字 | 保留 | 地址类型 | 地址 | 端口 |
项目长度 | 1 | 1 | X"00" | 1 | 不固定 | 2 |
版本固定 X"05";命令字分三种: CONNECT X"01",BIND X"02",UDP X"03"。CONNECT就是普通的TCP连接;BIND要求你的client支持接受server的连接请求(FTP协议就是一个典型的例子);UDP则是一个特例,我还没有完全理解… 保留项固定是 X"00"。
地址类型有三种:X"01"、X"03"、X"04",分别对应IP-V4、DOMAINNAME、IP-V6,而接下来的地址长度也根据地址类型的不同而变化。IP-V4的长度是4位,DOMAINNAME的长度则根据实际情况变化,但是地址的第一位的内容要设成域名字符串的长度,IP-V6就是16位。
端口长度固定两位,没什么可说的。
而server返回的内容格式也大致相同
项目 | 版本 | 返回值 | 保留 | 地址类型 | 地址(BND) | 端口 |
项目长度 | 1 | 1 | X"00" | 1 | 不固定 | 2 |
返回值可能是下列值中的一个:
连接方式 | 含义 |
X’00’ | 成功 |
X’01’ | general SOCKS server failure |
X’02’ | 连接不符合server规格 |
X’03’ | 目标网络无法到达 |
X’04’ | 目标主机无法到达 |
X’05’ | 连接拒绝 |
X’06’ | TTL expired |
X’07’ | 命令不支持 |
X’08’ | 地址格式不支持 |
X’09 to X’FF’ | 保留 |
估计各位看完上面的解读之后仍然是一头雾水,那么我就来贴一段代码,大家就明白了
///////////////////////////////////////////////////////////////////
//
// socks 5 范例
//
//
unsigned char command[10];//准备连接命令字
memset(command,0,10);
command[0]=5;//版本号 05
command[1]=m_bUseSocks5Logon?2:1;//如果需要验证的话,要发送两位方式字
command[2]=m_bUseSocks5Logon?2:0;
TRY
{
Send(command,m_bUseSocks5Logon?4:3,0);
int num=Receive(command,2);
if (num!=2)
{
m_nProxyError=PROXYERROR_REQUESTFAILED;
return FALSE;
}
}
CATCH_ALL(e)
{
m_nProxyError=PROXYERROR_REQUESTFAILED;
return FALSE;
}
END_CATCH_ALL
if (command[1]==0xFF)
{
m_nProxyError=PROXYERROR_AUTHREQUIRED;// 0xFF表示失败,没有合适的连接方式
return FALSE;
}
if (command[1])
{
if (command[1]!=2)
{
m_nProxyError=PROXYERROR_AUTHTYPEUNKNOWN;// 验证方式未知
return FALSE;
}
if (m_bUseSocks5Logon)
{
unsigned char *buffer=new unsigned
char[3+m_ProxyUser.GetLength()+m_ProxyPass.GetLength()];
sprintf((char *)buffer," %s %s",m_ProxyUser,m_ProxyPass);//分配用户名密码缓冲区
buffer[0]=5;
buffer[1]=m_ProxyUser.GetLength();
buffer[2+m_ProxyUser.GetLength()]=m_ProxyPass.GetLength();
TRY
{
Send(buffer,3+m_ProxyUser.GetLength()+m_ProxyPass.GetLength(),0);
//Get auth response
int num=Receive(command,2);
if (num!=2)
{
delete [] buffer;
m_nProxyError=PROXYERROR_AUTHFAILED;
return FALSE;
}
}
CATCH_ALL(e)
{
delete [] buffer;
m_nProxyError=PROXYERROR_AUTHFAILED;
return FALSE;
}
END_CATCH_ALL
if (command[1]!=0x00)
{
delete [] buffer;
m_nProxyError=PROXYERROR_AUTHFAILED;
return FALSE;
}
delete [] buffer;
}
else
{
m_nProxyError=PROXYERROR_AUTHNOLOGON;
return FALSE;
}
}
//构建请求
memset(command,0,10);
command[0]=5;
command[1]=1;
command[2]=0;
command[3]=1;
memcpy(&command[4],&sockAddr->sin_addr.S_un.S_addr,4);
memcpy(&command[8],&sockAddr->sin_port,2);
//上面只提供了IP-4地址方式,其他的可以自行更改代码
TRY
{
Send(command,10,0);
int num=Receive(command,10);
if (num!=10)
{
m_nProxyError=PROXYERROR_REQUESTFAILED;
return FALSE;
}
}
CATCH_ALL(e)
{
m_nProxyError=PROXYERROR_REQUESTFAILED;
return FALSE;
}
END_CATCH_ALL
if (command[1]!=0x00)
{
m_nProxyError=PROXYERROR_REQUESTFAILED;
return FALSE;
}
总之就是这个样子了,如果还有不明白的地方.