当前位置:首页 >> 编程开发 >> Visual C++ >> 内容

Socks5代理服务

时间:2009/11/8 作者:平凡之路 来源:xuhantao.com 浏览:

假期的时候把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;
}

总之就是这个样子了,如果还有不明白的地方.

相关文章
  • 没有相关文章
  • 徐汉涛(www.xuhantao.com) © 2024 版权所有 All Rights Reserved.
  • 部分内容来自网络,如有侵权请联系站长尽快处理 站长QQ:965898558(广告及站内业务受理) 网站备案号:蒙ICP备15000590号-1