far

级别:中级站友 威望:0 经验:0 货币:387 体力: 来源:61.174.138.* 总发帖数:60 注册日期:2002-07-03
|
|
查看 邮件 主页 QQ 消息 引用 复制 下载
局域网内都有部分机器既可以访问局域网,又可以访问互联网,如果能在该类机器上建立一个socks5代理服务器,那么局域网内的其他机器就可以通过它访问外面的机器,或者访问另一个局域网内的机器(需要二级代理)。实现代理功能的软件有很多,比如wingate,ccproxy,代理猎手和忆特代理软件等等,黑客代理软件有sksock。其中sksock大家肯定非常熟悉了,该软件功能强大,可以实现多级代理,但没有调度和控制功能。
下面是netpeer软件中实现socks5代理的一些主要的实现代码: // Sock5Thread.cpp:
implementation of the CSock5Thread class. //
//////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "Sock5Thread.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSock5Thread::CSock5Thread( int iListenPort ) {
m_iPort = iListenPort;
m_bStop = TRUE; }
CSock5Thread::~CSock5Thread() {
}
DWORD CSock5Thread::MainThread2(LPVOID pData)
{ m_bStop = FALSE;
BOOL bRet = CreateListenSocket(m_iPort); TRY
{
if(bRet)
{
while(!m_bStop)
{
CSockAddr saClient;
CPersonInstance sConnect;
if(psockListen.Accept(sConnect,saClient))
{//连接建立成功
CSock5ChildThread* pChild =
new CSock5ChildThread();
pChild->m_pUser->m_hSocket = sConnect.m_hSocket;
memcpy(pChild->m_pUser->m_IP,saClient.DottedDecimal(),15);
if(pChild->CreateNewThread(TRUE))
{
pChild->SetThreadPriority(
THREAD_PRIORITY_NORMAL);
}
else
{//无法创建子线程
delete pChild;
}
}
else
{//连接失败
break;
}
}
} }
CATCH(CBlockingSocketException, e)
{ #ifdef _DEBUG
AfxMessageBox("socks5 thread except"); #endif
} END_CATCH
psockListen.Cleanup();
m_bStop = true; return
0; }
void CSock5Thread::StopSocks() {
psockListen.Cleanup();
m_bStop = TRUE; }
BOOL CSock5Thread::IsRuning() {
return !m_bStop; }
BOOL
CSock5Thread::CreateListenSocket(int iPort) {
BOOL bRet = TRUE; TRY
{
CSockAddr saServer(INADDR_ANY, iPort);
psockListen.Create();
psockListen.Bind(saServer);
psockListen.Listen(); }
CATCH(CBlockingSocketException, e)
{
psockListen.Cleanup();
bRet = FALSE;
} END_CATCH
return bRet; }
//////////////////////////////////////////////////////////////////////
// CSock5ChildThread Class
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSock5ChildThread::CSock5ChildThread() {
m_pUser = NULL;
m_pUser = new CPersonInstance; m_pServer =
NULL; m_pServer = new CProxySocket; }
CSock5ChildThread::~CSock5ChildThread() {
if(m_pUser)delete m_pUser;
if(m_pServer)delete m_pServer; }
DWORD CSock5ChildThread::MainThread2(LPVOID pData) {
char* pRecBuf = new char[100];
memset(pRecBuf, 0, 100);
unsigned long nRecBuf = 0;
//开始不停地接受数据
TRY
{
//第一步,确认是sock5请求
nRecBuf =
m_pUser->Receive(pRecBuf, 100, 30);
if((pRecBuf[0] == 5 ) && ( ( nRecBuf == 3) || (nRecBuf ==
4))) {//sock5连接
//送5,0两个字节给客户端
pRecBuf[0] = 5;
pRecBuf[1] = 0; //不需要认证
m_pUser->Print(pRecBuf, 2);
} else
{//退出
m_pUser->Cleanup();
if(pRecBuf)delete []pRecBuf;
pRecBuf
= NULL;
delete this;
return 0;
}
//第二步,获取目的服务器的地址和端口
nRecBuf =
m_pUser->Receive(pRecBuf, 100, 30);
if(( pRecBuf[0] == 5 ) && ( pRecBuf[1] ==
1 ) && (nRecBuf > 4))
{//socks5,connect
if((pRecBuf[3] == 1) ||
(pRecBuf[3] == 3))
{//只处理ip4和domainame两种情况,ip6不处理
if(pRecBuf[3] == 1)
{//ip4
//转换为字符串格式的ip地址
unsigned long host;
memcpy(&host, pRecBuf+4,
4);
host = ntohl(host);
UINT port;
memcpy(&port, pRecBuf+8,
2);
port = ntohs(port);
CSockAddr addr =
CSockAddr(host, port);
m_strServerIp = addr.DottedDecimal();
m_iServerPort = port;
}
if(pRecBuf[3] == 3)
{//domainname
int iServerNameLen =
pRecBuf[4];
char*
chServerIp = new char[iServerNameLen+1];
memset(chServerIp, 0, iServerNameLen+1);
memcpy(chServerIp, pRecBuf+5,
iServerNameLen);
m_strServerIp = (CString)chServerIp;
if(chServerIp)delete []chServerIp;
memcpy(&m_iServerPort,
pRecBuf+iServerNameLen+5, 2);
m_iServerPort= ntohs(m_iServerPort);
}
//回应信息
if(ConnectToServer())
{
pRecBuf[1] = 0; //成功
pRecBuf[3] = 1; //P V4 address
CSockAddr SockAddr;
m_pServer->GetSockAddr(SockAddr);
ULONG ulHost = SockAddr.IPAddr();
USHORT usPort =
SockAddr.Port();
memcpy(pRecBuf+4, &ulHost, 4);
memcpy(pRecBuf+8, &usPort, 2);
//首先启动与双方通讯的线程
CSocks5ServerClientThread* pServerClientThread =
new CSocks5ServerClientThread();
pServerClientThread->m_pUser->m_hSocket =
m_pUser->m_hSocket;
pServerClientThread->m_pServer->m_hSocket =
m_pServer->m_hSocket;
if(pServerClientThread->CreateNewThread(TRUE))
{
pServerClientThread->SetThreadPriority( THREAD_PRIORITY_NORMAL);
}
else
{//无法创建子线程
delete
pServerClientThread;
}
CSocks5ClientServerThread* pClientServerThread = new
CSocks5ClientServerThread();
pClientServerThread->m_pUser->m_hSocket =
m_pUser->m_hSocket;
pClientServerThread->m_pServer->m_hSocket =
m_pServer->m_hSocket;
if(pClientServerThread->CreateNewThread(TRUE))
{
pClientServerThread->SetThreadPriority( THREAD_PRIORITY_NORMAL);
}
else
{//无法创建子线程
delete
pClientServerThread;
}
m_pUser->Print(pRecBuf,
10);
}
else
{//连接到目的服务器失败
pRecBuf[1]
= 4; // Host unreachable
m_pUser->Print(pRecBuf, nRecBuf);
m_pUser->Cleanup();
if(pRecBuf)delete []pRecBuf;
pRecBuf = NULL;
delete this;
return 0;
}
}
else
{//ipv6或者其他则退出
m_pUser->Cleanup();
if(pRecBuf)delete []pRecBuf;
pRecBuf = NULL;
delete
this;
return 0;
}
} else
if(( pRecBuf[0] == 5 ) && ( pRecBuf[1] == 3 ) &&
(nRecBuf > 4))
{//socks5,UDP
if((pRecBuf[3] == 1) || (pRecBuf[3] == 3))
{//只处理ip4和domainame两种情况,ip6不处理
if(pRecBuf[3] == 1)
{//ip4
//转换为字符串格式的ip地址
unsigned long host;
memcpy(&host, pRecBuf+4, 4);
host = ntohl(host);
UINT port;
memcpy(&port, pRecBuf+8,
2);
port = ntohs(port);
CSockAddr addr =
CSockAddr(host, port);
m_strServerIp = addr.DottedDecimal();
m_iServerPort = port;
}
if(pRecBuf[3] == 3)
{//domainname
int iServerNameLen =
pRecBuf[4];
char*
chServerIp = new char[iServerNameLen+1];
memset(chServerIp, 0, iServerNameLen+1);
memcpy(chServerIp, pRecBuf+5,
iServerNameLen);
m_strServerIp = (CString)chServerIp;
if(chServerIp)delete []chServerIp;
memcpy(&m_iServerPort,
pRecBuf+iServerNameLen+5, 2);
m_iServerPort= ntohs(m_iServerPort);
}
}
else
{
m_pUser->Cleanup();
if(pRecBuf)delete []pRecBuf;
pRecBuf = NULL;
delete
this;
return 0;
}
pRecBuf[1] = 0; //成功
pRecBuf[3] = 1; //P V4 address
m_pUdp.Create(SOCK_DGRAM);
CSockAddr saServer(INADDR_ANY, 0);
m_pUdp.Bind(saServer);
CSockAddr addr;
m_pUdp.GetSockAddr(addr);
// ULONG
ulHost = addr.IPAddr(); //UDP服务器无法获得本地的IP地址
USHORT usPort = addr.Port();
m_pUser->GetSockAddr(addr);
ULONG ulHost = addr.IPAddr();
ulHost
= htonl(ulHost);
usPort = htons(usPort);
memcpy(pRecBuf+4, &ulHost,
4);
memcpy(pRecBuf+8, &usPort, 2);
//首先启动与双方通讯的线程
CSocks5UdpThread* pServerClientThread = new CSocks5UdpThread();
pServerClientThread->m_pUdp.m_hSocket =
m_pUdp.m_hSocket;
m_pUser->GetPeerAddr(addr);
pServerClientThread->m_ulClientIp = addr.IPAddr(); //得到被代理者的ip
pServerClientThread->m_iClientPort = m_iServerPort;
//得到被代理者的服务器通讯的端口
if(pServerClientThread->CreateNewThread(TRUE))
{
pServerClientThread->SetThreadPriority( THREAD_PRIORITY_NORMAL);
}
else
{//无法创建子线程
delete pServerClientThread;
}
m_pUser->Print(pRecBuf, 10);
m_pUser->Cleanup(); }
else
{//退出
m_pUser->Cleanup();
if(pRecBuf)delete []pRecBuf;
pRecBuf
= NULL;
delete this;
return 0;
}
}
CATCH(CBlockingSocketException, e)
{
m_pUser->Cleanup(); }
END_CATCH
if(pRecBuf)delete []pRecBuf; pRecBuf = NULL;
delete this;
return 0; }
BOOL CSock5ChildThread::ConnectToServer()
{ TRY
{
m_pServer->Create();
m_pServer->SetProxy(PROXYTYPE_NOPROXY);
m_pServer->Connect(m_strServerIp, m_iServerPort);
return TRUE;
}
CATCH(CBlockingSocketException, e)
{ }
END_CATCH
return
FALSE; }
//////////////////////////////////////////////////////////////////////
// CSocks5ServerClientThread Class
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSocks5ServerClientThread::CSocks5ServerClientThread() {
m_pUser = NULL;
m_pUser = new CPersonInstance; m_pServer =
NULL; m_pServer = new CProxySocket; }
CSocks5ServerClientThread::~CSocks5ServerClientThread()
{ if(m_pUser)delete m_pUser;
if(m_pServer)delete m_pServer; }
DWORD CSocks5ServerClientThread::MainThread2(LPVOID pData)
{ char* pRecBuf = new char[5000];
memset(pRecBuf, 0, 5000);
unsigned long nRecBuf = 0;
while(1)
{
TRY
{
nRecBuf
= m_pServer->Receive(pRecBuf, 5000, 30);
if(nRecBuf <=
0)break; //对方已经断开了连接
m_pUser->Print(pRecBuf,
nRecBuf);
}
CATCH(CBlockingSocketException, e)
{
if(e->m_strMessage != "Receive timeout")break;
}
END_CATCH
}
m_pServer->Cleanup();
m_pUser->Cleanup();
if(pRecBuf)delete
[]pRecBuf; pRecBuf = NULL;
delete this; return 0;
}
//////////////////////////////////////////////////////////////////////
// CSocks5ClientServerThread Class
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSocks5ClientServerThread::CSocks5ClientServerThread() {
m_pUser = NULL;
m_pUser = new CPersonInstance; m_pServer =
NULL; m_pServer = new CProxySocket; }
CSocks5ClientServerThread::~CSocks5ClientServerThread()
{ if(m_pUser)delete m_pUser;
if(m_pServer)delete m_pServer; }
DWORD CSocks5ClientServerThread::MainThread2(LPVOID pData)
{ char* pRecBuf = new char[5000];
memset(pRecBuf, 0, 5000);
unsigned long nRecBuf = 0;
while(1)
{ TRY
{
nRecBuf =
m_pUser->Receive(pRecBuf, 5000, 30);
if(nRecBuf <=
0)break; //对方已经断开了连接
m_pServer->Print(pRecBuf,
nRecBuf);
}
CATCH(CBlockingSocketException, e)
{
if(e->m_strMessage != "Receive timeout")break;
}
END_CATCH
}
m_pServer->Cleanup();
m_pUser->Cleanup();
if(pRecBuf)delete
[]pRecBuf; pRecBuf = NULL;
delete this; return 0;
}
//////////////////////////////////////////////////////////////////////
// CSocks5UdpClientServerThread Class
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSocks5UdpThread::CSocks5UdpThread() { }
CSocks5UdpThread::~CSocks5UdpThread() { }
DWORD CSocks5UdpThread::MainThread2(LPVOID pData)
{ char* pRecBuf = new
char[5000]; unsigned long nRecBuf = 0;
unsigned long nPackHeadLen = 0; //数据包头的长度
while(1)
{
TRY {
memset(pRecBuf, 0, 5000);
nRecBuf = m_pUdp.ReceiveDatagram(pRecBuf+10,
5000, saRec, 1000);
if(nRecBuf <= 0)break;
//对方已经断开了连接
if(saRec.Port() == m_iClientPort)
{//来自被代理者的数据包
//首先解包判断
if((pRecBuf[13] == 1) || (pRecBuf[13] == 3))
{//只处理ipv4和domainame两种情况,ip6不处理
if( pRecBuf[13] == 1)
{//ipv4
//转换为字符串格式的ip地址
unsigned
long host;
memcpy(&host, pRecBuf+14, 4);
host =
ntohl(host);
UINT port;
memcpy(&port, pRecBuf+18,
2);
port =
ntohs(port);
saSend = CSockAddr(host, port);
nPackHeadLen = 10;
}
else if( pRecBuf[13] == 3 )
{
int iServerNameLen =
pRecBuf[14];
char* chServerIp = new char[iServerNameLen+1];
memset(chServerIp, 0, iServerNameLen+1);
memcpy(chServerIp, pRecBuf+15,
iServerNameLen);
UINT m_iServerPort;
memcpy(&m_iServerPort,
pRecBuf+iServerNameLen+15, 2);
m_iServerPort =
ntohs(m_iServerPort);
saSend =
CBlockingSocket::GetHostByName(chServerIp, m_iServerPort);
if(chServerIp)delete []chServerIp;
nPackHeadLen = iServerNameLen + 7;
}
else
{
}
m_pUdp.SendDatagram(pRecBuf+10+nPackHeadLen, nRecBuf-nPackHeadLen,
saSend); //送给目的服务器
}
}
else
{//来自目的服务器的数据包
//首先打包
pRecBuf[3] = 1;
UINT iServerIp = saRec.IPAddr();
memcpy(pRecBuf+4, &iServerIp, 4);
UINT iPort = saRec.Port();
memcpy(pRecBuf+8, &iPort, 2);
saSend =
CSockAddr(m_ulClientIp, m_iClientPort);
m_pUdp.SendDatagram(pRecBuf, nRecBuf+10, saSend);
}
}
CATCH(CBlockingSocketException, e)
{
if(e->m_strMessage != "Receive timeout")
{
break;
}
}
END_CATCH }
m_pUdp.Cleanup();
if(pRecBuf)delete []pRecBuf; pRecBuf = NULL;
delete this; return 0;
}
附录: RFC1928(部分) 1.介绍
利用网络防火墙可以将组织内部的网络结构从外部网络如INTERNET中有效地隔离,这种方法在许多网络系统中正变得流行起来。这种防火墙系统通常以应用层网关的形式工作在两个网络之间,提供TELNET、FTP、SMTP等的接入。随着越来越多的使全球信息查找更容易的复杂的应用层协议的出现,有必要提供一个通用框架来使这些协议安全透明地穿过防火墙。而且在实际应用中还需要一种安全的认证方式用以穿越防火墙。这个要求起源于两个组织的网络中客户/服务器关系的出现,这个关系需要得到控制并要求有安全的认证。
在这儿所描述的协议框架是为了让使用TCP和UDP的客户/服务器应用程序更方便安全地使用网络防火墙所提供的服务所设计的。这个协议从概念上来讲是介于应用层和传输层之间的“中介层(shim-layer)”,因而不提供如传递ICMP信息之类由网络层网关的所提供的服务。
2.现有的协议 当前存在一个协议SOCKS
4,它为TELNET、FTP、HTTP、WAIS和GOPHER等基于TCP协议的客户/服务器程序提供了一个不安全的防火墙。而这个新的协议扩展了SOCKS
V4,以使其支持UDP、框架规定的安全认证方案、地址解析方案(addressing
scheme)中所规定的域名和IPV6。为了实现这个SOCKS协议,通常需要重新编译或者重新链接基于TCP的客户端应用程序以使用SOCKS库中相应的加密函数。
注意:
除非特别注明,所有出现在数据包格式图中的十进制数字均以字节表示相应域的长度。如果某域需要给定一个字节的值,用X’hh’来表示这个字节中的值。如果某域中用到单词’Variable’,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1
– 2个字节)的域中,或一个数据类型域中。
3.基于TCP协议的客户
当一个基于TCP协议的客户端希望与一个只能通过防火墙可以到达的目标(这是由实现所决定的)建立连接,它必须先建立一个与SOCKS服务器上SOCKS端口的TCP连接。通常这个TCP端口是1080。当连接建立后,客户端进入协议的“握手(negotiation)”过程:认证方式的选择,根据选中的方式进行认证,然后发送转发的要求。SOCKS服务器检查这个要求,根据结果,或建立合适的连接,或拒绝。
除非特别注明,所有出现在数据包格式图中的十进制数字均以字节表示相应域的长度。如果某域需要给定一个字节的值,用X’hh’来表示这个字节中的值。如果某域中用到单词’Variable’,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1
– 2个字节)的域中,或一个数据类型域中。 客户端连到服务器后,然后就发送请求来协商版本和认证方法:
VER NMETHODS METHODS
1 1 1 to 255
这个版本的SOCKS协议中,VER字段被设置成X'05'。NMETHODS字段包含了在METHODS字段中出现的方法标示的数目(以字节为单位)。
服务器从这些给定的方法中选择一个并发送一个方法选中的消息回客户端: VER
METHOD 1 1
如果选中的消息是X’FF’,这表示客户端所列出的方法列表中没有一个方法被选中,客户端必须关闭连接。 当前定义的方法有:
· X’00’
不需要认证 · X’01’
GSSAPI · X’02’
用户名/密码 ·
X’03’ -- X’7F’ 由IANA分配 ·
X’80’ -- X’FE’ 为私人方法所保留的 ·
X’FF’ 没有可以接受的方法
然后客户和服务器进入由选定认证方法所决定的子协商过程(sub-negotiation)。各种不同的方法的子协商过程的描述请参考各自的备忘录。
开发者如果要为自己的方法得到一个方法号,可以联系IANA。可以参考关于已经被分配号码的文档以得到当前所有方法的列表和相应的协议。
符合本文档的SOCKS V5实现必须支持GSSAPI,并且在将来支持用户名/密码认证方式。
4.请求
一旦子协商过程结束后,客户端就发送详细的请求信息。如果协商的方法中有以完整性检查和/或安全性为目的的封装,这些请求必须按照该方法所定义的方式进行封装。
SOCKS请求的格式如下: VER CMD
RSV ATYP
DST.ADDR DST.PORT 1
1 X’00’ 1
Variable 2 其中
· VER 协议版本: X’05’ · CMD
· CONNECT:X’01’ ·
BIND:X’02’ · UDP ASSOCIATE:X’03’
· RSV 保留 · ATYP 后面的地址类型
· IPV4:X’01’ · 域名:X’03’
· IPV6:X’04’' · DST.ADDR
目的地址 · DST.PORT 以网络字节顺序出现的端口号
SOCKS服务器会根据源地址和目的地址来分析请求,然后根据请求类型返回一个或多个应答。
5.地址
ATYP字段中描述了地址字段(DST.ADDR,BND.ADDR)所包含的地址类型:
· X'01' 基于IPV4的IP地址,4个字节长
· X'03'
基于域名的地址,地址字段中的第一字节是以字节为单位的该域名的长度,没有结尾的NUL字节。
· X'04' 基于IPV6的IP地址,16个字节长
6.应答
一旦建立了一个到SOCKS服务器的连接,并且完成了认证方式的协商过程,客户机将会发送一个SOCKS请求信息给服务器。服务器将会根据请求,以如下格式返回:
VER REP
RSV ATYP
BND.ADDR BND.PORT 1
1 X’00’ 1
Variable 2 其中: · VER
协议版本: X’05’ · REP 应答字段:
· X’00’ 成功
· X’01’ 普通的SOCKS服务器请求失败
· X’02’ 现有的规则不允许的连接
· X’03’ 网络不可达
· X’04’ 主机不可达
· X’05’ 连接被拒
· X’06’ TTL超时
· X’07’ 不支持的命令
· X’08’ 不支持的地址类型
· X’09’ – X’FF’ 未定义
· RSV 保留 · ATYP 后面的地址类型
· IPV4:X’01’ · 域名:X’03’
· IPV6:X’04’ · BND.ADDR
服务器绑定的地址 · BND.PORT 以网络字节顺序表示的服务器绑定的段口
标识为RSV的字段必须设为X’00’。
如果选中的方法中有以完整性检查和/或安全性为目的的封装,这些应答必须按照该方法所定义的方式进行封装。
CONNECT
在对一个CONNECT命令的应答中,BND.PORT包含了服务器分配的用来连到目标机的端口号,BND.ADDR则是相应的IP地址。由于SOCKS服务器通常有多个IP,应答中的BND.ADDR常和客户端连到SOCKS服务器的那个IP不同。
SOCKS服务器可以利用DST.ADDR和DST.PORT,以及客户端源地址和端口来对一个CONNECT请求进行分析。
BIND
BIND请求通常被用在那些要求客户端接受来自服务器的连接的协议上。FTP是一个典型的例子。它建立一个从客户端到服务器端的连接来执行命令以及接收状态的报告,而使用另一个从服务器到客户端的连接来接收传输数据的要求(如LS,GET,PUT)。
建议只有在一个应用协议的客户端在使用CONNECT命令建立主连接后才可以使用BIND命令建立第二个连接。建议SOCKS服务器使用DST.ADDR和DST.PORT来评价BIND请求。
在一个BIND请求的操作过程中,SOCKS服务器要发送两个应答给客户端。当服务器建立并绑定一个新的套接口时发送第一个应答。BND.PORT字段包含SOCKS服务器用来监听进入的连接的端口号,BAND.ADDR字段包含了对应的IP地址。客户端通常使用这些信息来告诉(通过主连接或控制连接)应用服务器连接的汇接点。第二个应答仅发生在所期望到来的连接成功或失败之后。在第二个应答中,BND.PORT和BND.ADDR字段包含了连上来的主机的IP地址和端口号。
UDP ASSOCIATE UDP
ASSOCIATE请求通常是要求建立一个UDP转发进程来控制到来的UDP数据报。DST.ADDR和DST.PORT
字段包含客户端所希望的用来发送UDP数据报的IP地址和端口号。服务器可以使用这个信息来限制进入的连接。如果客户端在发送这个请求时没有地址和端口信息,客户端必须用全0来填充。
当与UDP相应的TCP连接中断时,该UDP连接也必须中断。 应答UDP ASSOCIATE请求时,BND.PORT
和BND.ADDR字段指明了客户发送UDP消息至服务器的端口和地址。
应答处理
当一个应答(REP值不等于00)指明出错时,SOCKS服务器必须在发送完应答消息后一小段时间内终止TCP连接。这段时间应该在发现错误后少于10秒。
如果一个应答(REP值等于00)指明成功,并且请求是一个BIND或CONNECT时,客户端就可以开始发送数据了。如果协商的认证方法中有以完整性、认证和/或安全性为目的的封装,这些请求必须按照该方法所定义的方式进行封装。类似的,当以客户机为目的地的数据到达SOCKS服务器时,SOCKS服务器必须用正在使用的方法对这些数据进行封装。
7.基于UDP协议的客户 在UDP
ASSOCIATE应答中由BND.PORT指明了服务器所使用的UDP端口,一个基于UDP协议的客户必须发送数据报至UDP转发服务器的该端口上。如果协商的认证方法中有以完整性、认证和/或安全性为目的的封装,这些数据报必须按照该方法所定义的方式进行封装。每个UDP数据报都有一个UDP请求头在其首部:
RSV FRAG
ATYP DST.ADDR
DST.PORT DATA 2
1 1 Variable
2 Variable 在UDP请求头中的字段是:
· RSV 保留 X’0000’ ·
FRAG 当前的分段号 · ATYP 后面的地址类型
· IPV4:X’01’ · 域名:X’03’
· IPV6:X’04’ · DST.ADDR
目的地址 · DST.PORT 以网络字节顺序出现的端口号
· DATA 用户数据
当一个UDP转发服务器转发一个UDP数据报时,不会发送任何通知给客户端;同样,它也将丢弃任何它不能发至远端主机的数据报。当UDP转发服务器从远端服务器收到一个应答的数据报时,必须加上上述UDP请求头,并对数据报进行封装。
UDP转发服务器必须从SOCKS服务器得到期望的客户端IP地址,并将数据报发送到UDP
ASSOCIATE应答中给定的端口号。如果数据报从任何IP地址到来,而该IP地址与该特定连接中指定的IP地址不同,那么该数据报会被丢弃。
FRAG字段指明数据报是否是一些分片中的一片。如果SOCKS服务器要实现这个功能,X’00’指明数据报是独立的;其他则越大越是数据报的尾端。介于1到127之间的值说明了该分片在分片序列里的位置。每个接收者都为这些分片提供一个重组队列和一个重组的计时器。这个重组队列必须在重组计时器超时后重新初始化,并丢弃相应的数据报。或者当一个新到达的数据报有一个比当前在处理的数据报序列中最大的FRAG值要小时,也必须重新初始化从组队列。重组计时器必须小于5秒。只要有可能,应用程序最好不要使用分片。
分片的实现是可选的;如果某实现不支持分片,所有FRAG字段不为0的数据报都必须被丢弃。
一个SOCKS的UDP编程界面(The programming interface for a SOCKS-aware
UDP)必须报告当前可用UDP数据报缓存空间小于操作系统提供的实际空间。 · 如果
ATYP是 X’01’ - 10+method_dependent octets smaller
· 如果 ATYP是X’03’ - 262+method_dependent octets
smaller · 如果 ATYP是X’04’ - 20+method_dependent
octets smaller
RFC1929(部分) SOCKS V5的用户名/密码鉴定
(RFC1929 Username/Password Authentication for SOCKS V5)
本备忘录状态:
本文档讲述了一种Internet社区的Internet标准跟踪协议,它需要进一步进行讨论和建议以得到改进。请参考最新版的“Internet正式协议标准”
(STD1)来获得本协议的标准化程度和状态。本备忘录的发布不受任何限制。 1. 介绍
关于SOCKS
V5的协议规范说明了在初始化SOCKS连接时所用到的任意验证协议的大致框架。这篇文档描述了这些协议中的其中一个适合SOCKS
V5验证子协商(subnegotiation)。 注意:
除非特别注明,所有出现在数据包格式图中的十进制数字均以字节表示相应域的长度。如果某域需要给定一个字节的值,用X’hh’来表示这个字节中的值。如果某域中用到单词’Variable’,这表示该域的长度是可变的,且该长度定义在一个和这个域相关联(1
– 2个字节)的域中,或一个数据类型域中。 2.初始协商 一旦SOCKS
V5服务器运行并且客户端选择了用户名/密码认证协议以后,就开始了用户名/密码协议的子协商过程。客户端先产生一个用户名/密码协议的请求:
VER ULEN
UNAME PLEN PASSWD
1 1 1 to
255 1 1 to 255
VER中指明了子协商的当前版本,现在使用的是X’01’。ULEN域中包含了下一个UNAME域的长度。UNAME中包含一个源操作系统(source
operating
system)所知道的用户名。PLEN中指明了紧随其后的PASSWD的长度。PASSWD中则包含了对应UNAME用户的密码。
服务器验证用户名和密码,并且返回: VER STATUS
1 1
如果STATUS中返回X’00’则说明通过验证。如果服务器返回非X’00’则说明验证失败,并且关闭连接。 3.安全考虑
这篇文档描述了为SOCKS
V5协议提供验证服务的子协商过程。因为密码是以明文传输的,所以这个子协商过程在可能被工具“嗅探(sniffing)”到的环境中不建议使用该子协商过程。
4.作者地址 Marcus Leech Bell-Northern Research Ltd P.O.
Box 3511, Station C Ottawa, ON CANADA K1Y 4H7 Phone: +1
613 763 9145 EMail: mleech@bnr.ca
|