程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> Socks5代理服務

Socks5代理服務

編輯:關於C++

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

總之就是這個樣子了,如果還有不明白的地方.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved