socket4和socket5的區別

socket5對於  socket4的改進

關鍵詞 socket5       socket4                                          

        網絡編程中,對於數據傳輸實時性要求較高的場合,大家都會選擇UDP來作爲數據傳輸協議,在TCP/IP協議族中UDP協議較TCP協議需要的網絡系統資源更少。然而在企業應用中,由於網絡安全原因等會導致除了特定端口以外的IP數據無法通過專用的路由或網關。爲了支持這類應用,制定了專門的支持Socks連接的socks4/socsk5協議。Socks協議允許實現此類功能的代理軟件可以允許防火牆(本文以下內容中防火牆代理的稱謂可以等同視之)以內的客戶通過防火牆實現對外部的訪問,甚至可以允許等待外部的連接。對於防火牆內部的軟件客戶端,僅同防火牆協商,同防火牆的特定端口取得聯繫,然後交換數據,而防火牆外部的程序也直同防火牆進行數據交換,外部看不到防火牆的內部網情況,這樣起到了防火牆的監護功能,也滿足了大多數通過非常用(如http ftp等)端口交換數據的應用程序需求。防火牆內部的應用程序如何通過防火牆將UDP數據傳輸到防火牆外部,並且接受外部的UDP數據報文,這就是所謂穿透Socks代理的UDP編程。

  RFC1928描述了Socks協議的細節,告訴我們客戶程序如何同Socks代理協商,取得透過該協議對外傳輸的途徑。英文的URL爲:http://www.ietf.org/rfc/rfc1928.txt,中文的翻譯參考不是很貼切(但譯者還是值得尊敬的),但對於E文不大好的可以將就一下:http://www.china-pub.com/computers/emook/0541/info.htm。建議先了解以上鍊接內容後在閱讀下文。

  一般的代理軟件都實現了兩個版本的Socks協議—Socks4以及Socks5,其中Socks5協議支持UDP報文的傳輸以及多種驗證方法,該協議還考慮IP發展需要,支持Ipv6。

  TCP透過代理支持兩種方法:Bind以及Connection。Connection是指作爲客戶端,主動連接代理外部的服務程序,在這種方式中代理將替代客戶程序發起真正的對外部服務程序的連接,並來回傳輸在此連接中需要交流的數據;Bind方式則用在那些需要客戶機接受到服務器連接的協議中,例如FTP協議之類的除了需要建立一個客戶--服務器的連接報告狀態外,還需要建立一個服務器—客戶的連接來傳輸實際的數據(當然要注意這裏的FTP協議通過Socks協議連接遠程主機,並非通過FTP代理協議)。UDP報文傳輸則意味着代理充當UDP數據傳輸的中間人,將防火牆內的主機對外的數據傳遞出去,將需要引入到防火牆內的UDP數據報文轉給防火牆特定的主機。關於TCP穿透的討論和例子很多(給出一個實現的例子:http://www.codeproject.com/internet/casyncproxysocket.asp ),就不多講了,在此着重討論如何實現UDP數據的穿透Socks5代理。

  爲了測試方便我簡單寫了一個服務進程在代理外IP爲192。168。0。250上監聽UDP8100端口,接收到一個UDP的數據報後,返回服務器上的當前時間給發送UDP報文的客戶端。代理採用Wingate,他在192。168。0。1上運行,Socks的標準端口1080來運行服務監聽程序。我的機器爲192。168。0。10,你可以看到,我不能夠直接聯繫運行時間服務的機器,我會向代理提出我的要求,由代理進程負責UDP數據報文的轉發。代理軟件選擇Wingate,並且爲了簡單起見,採用不需要驗證客戶的驗證方法。好了,交代了背景後,下面我們就開始穿越代理的旅程吧。

  無論是TCP還是UDP通過代理,首先要同代理取得聯繫。爲了能夠確保在第一階段順利確保數據傳輸,協議規定客戶端採用TCP方式連接聯繫代理服務器。

  一旦客戶同代理的1080端口連接上,客戶首先要發送一個版本標識/方法選擇的TCP報文給代理服務器,具體格式爲:

  版本號(1字節) | 可供選擇的認證方法(1字節) | 方法序列(1-255個字節長度)

  如果是socks4協議,版本號就是0x04,但是這裏是支持UDP的Socks5,所以是字節0x05。此說明對於後面的報文格式解釋的版本部分也都適用。

  Socks協議定義了0-255種通過代理的認證方法:

  0x00 無驗證需求

  0x01 通用安全服務應用程序接口(GSSAPI)

  0x02 用戶名/密碼(USERNAME/PASSWORD)

  0x03 至 X'7F' IANA 分配(IANA ASSIGNED)

  0x80 至 X'FE' 私人方法保留(RESERVED FOR PRIVATE METHODS)

  0xFF 無可接受方法(NO ACCEPTABLE METHODS)

  顯然,無論是發起Socks請求的客戶端還是負責轉發Socks數據的代理都不可能完全實現所有的(起碼目前還沒有)方法,所以客戶端需要把自己能夠支持的方法列出來供代理服務器選擇。如果支持無驗證,那麼此報文的字節序列就爲:0x05 0x01 0x00,其中的0x01表示客戶端只支持一種驗證,0x00表示能夠支持的方法是編號爲0x00的(無驗證)的方法。如果客戶端還支持用戶名/密碼的驗證方式,那麼報文就應當是:0x05 0x02 0x00 0x02。

  代理接收到客戶的請求,會根據自身系統的實現返回告訴客戶驗證採用哪一種方法,返回的保文格式爲:

版本號 | 服務器選定的方法

  如果服務器僅支持無驗證的驗證方法,它返回字節序列:0x05 0x00。客戶端同代理的數據報文的來回應答就是Socks協議的驗證方法選擇階段。

  接下來就是根據選擇的方法來,驗證客戶身份了。雖然我們這裏不需要驗證,但是還是簡單講一下0x02的用戶名/口令的驗證客戶端發送報文格式:

  0x01 | 用戶名長度(1字節)| 用戶名(長度根據用戶名長度域指定) | 口令長度(1字節) | 口令(長度由口令長度域指定)

  不清楚爲什麼報文的首字節是0x01(按照慣例應當是0x05)。整個報文長度根據用戶名和口令的實際長度決定。用戶名和口令都不需要以’\0’結束。服務器會根據提供的信息進行驗證,返回如下的報文字節序列映像爲:

  0x01 | 驗證結果標誌

  驗證結果標誌可以爲:0x00 驗證通過,其餘均表示有故障,不可以繼續下一步的協議步驟。

  在通過了驗證步驟之後,接下來就是確定UDP傳輸的端口了。這裏面需要確定兩個重要的端口:1、客戶端發送UDP數據的本機端口,一方面可以爲發送數據指定端口,另一方面告訴代理,如果有數據返回,就傳遞給該端口,構成一個UDP傳輸迴路。2、代理想在哪個端口接收客戶發送的UDP數據報,作爲對外UDP Socket的申請方,雙方協商確定一個端口後,可以持續通過此端口向外部主機發送數據,也可以通過此端口由代理接收外部主機發回的UDP數據,再通過此端口發給UDP發送請求客戶端。客戶端會按照以下格式發送TCP數據字節序列:

  協議版本 | Socks命令 | 保留字節 | 地址類型 | 特定地址 | 特定端口

  Socks命令有3種:CONNECT (編號0x01) BIND (0x02) UDP(編號0x03)

  保留字節長度1,爲0x00

  地址類型有3種:

  0X01  該地址是IPv4地址,長4個8bit字節。

  0X03  該地址包含一個完全的域名。第一個8bit字節包含了後面名稱的8bit的數目,沒有中止的’\0’。

  0X04 該地址是IPv6地址,長16個8bit節。

  特定地址一般對於多IP的主機有意義,如果不是或者不關心哪一個IP發起UDP數據傳輸,就可以填0。0。0。0,地址類型選擇0x01。比較重要的就是UDP傳輸將要從哪一個UDP端口發起。一般爲了避免因爲硬性指定一個端口導致引起衝突,會首先生成一個UDP套接字,用生成的套接字既定端口來作爲自己傳輸UDP的端口,並通過此步驟告知代理服務器。譬如臨時生成一個UDP套接字,UDP選擇端口2233作爲傳輸UDP數據的本地端口,那麼此報文就爲:0x05 0x03 0x00 0x00 0x00 0x00 0x00 0x08 0xb9 其中0x08 0xb9換算成10進制就是2233。

  代理服務器會根據自己的端口占用情況,給出一個有關代理服務器的端口的回覆字節序列,告訴客戶可以將UDP數據發送到此地址和端口中去,以實現UDP穿透代理。返回的字節序列爲:

  版本 | 代理的應答 | 保留1字節 | 地址類型 | 代理服務器地址 | 綁定的代理端口

  代理的應答可以爲值:

  0X00 成功協商

  0X01 常見的Socks故障

  0x02 不允許連接

  0X03 網絡不可到達

  0X04 主機不可到達

  0X05 連接被重置

  0X06 TTL 失效

  0X07 命令不支持

  0X08 地址類型不支持

  0X09 一直到0xff都保留

  代理的地址指客戶端需要發給那一個IP,綁定的端口指代理將在哪一個端口上爲客戶接收數據並轉發出去。地址類型、地址參照上面的解釋。

  通過以上的TCP協商幾個步驟後,現在客戶端明確了自己將需要發送的UDP數據發給代理服務器的某個IP的某個端口了。代理服務器也知道是哪一個IP發送數據報給自己,如果接收到由於轉發此UDP數據報而從遠端目標主機傳回的數據報,他需要根據協議將收到的數據報返回給客戶的特定端口。此特定端口就是此步驟中字節序列中綁定的代理端口

  在傳輸UDP數據時,由於通過代理,所以需要按照一定的格式進行包裝,在需要傳送的數據之前添加一個報頭,具體爲:

  保留2字節的0 | 是否數據報分段重組標誌 | 地址類型 | 將要發到代理外的目標地址 | 遠端目標主機的端口 | 需要通過代理傳送出去的數據

  是否數據報分段重組標誌爲0表示該數據報文是獨立的不需要重新組合,其他的表示特定的序列號,以利於UDP報文整合。

  這裏的地址是最終接收此UDP數據的代理外的服務器地址,我們這個例子中就是192。168。0。250。端口就是8100。根據地址類型不同,具體的需要傳送的數據起始位置也不同。如果是Ipv4,那麼數據從整個UDP報文的10字節處開始,如果是指定了域名,那麼就是從262處開始,Ipv6地址類型就從20處開始爲數據字段。這些需要我們在實際傳輸數據時注意。假如要傳送10字節的數據9到96.96.96.96的1024端口,那麼傳送的數據字節序列大致爲:

  00 00 00 01 60 60 60 60 04 00 09 09 09 …….09

  保留 是否分段重組 Ipv4 96.96.96.96目標主機IP 端口1024 從此處開始爲數據

  而這之後,如果遠端的目標主機有數據返回,代理服務器會在將數據傳回給UDP 客戶端時將數據也做類似上面的封裝,即添加一個報頭。客戶需要接收這個報頭,實際上也明確通知UDP 客戶端,這個數據報是哪一個服務器發回的。

  下面就來看一看給出的代碼:見附件工程。我將支持Socks5的UDP寫成一個Java類,供大家參考。相關解釋見註釋部分。

  通過上面分析我們可以大體上總結到透過Socks5進行UDP編程需要注意的幾點:

  1、 Socks5編程的身份驗證

  由於防火牆作用幾乎是隔絕內外的非正常連接,而Socket可以通過任何端口連接到外部,所以作爲對Socket4的改進,Socket5增加了對socket協議訪問的驗證功能。這些驗證功能沒有規定一定採用什麼方法,一般看防火牆自身支持以及客戶端能夠支持什麼方法,這意味着作爲客戶端必須將自己支持的方法在協商階段之初就告訴代理服務器,而代理服務器自己根據已經實現支持哪種驗證方法而選擇特定的方法回覆客戶端。意味着針對不同的代理服務器以及不同的客戶端,很可能對於驗證方法支持上有區別,需要視具體的應用環境而定。這些增加了Socket5客戶端以及Proxy server軟件的編寫難度,但是增強了安全性。

  2、 TCP保持重要性

  要發送穿透代理服務器的UDP數據報,其實首先需要建立客戶端到代理服務器的TCP連接,通過一系列的交互,獲得代理服務器的許可才能夠發送出去(同時代理服務器業記錄下連接的在Socks5服務的客戶IP和端口),也確保從遠端發回的數據能夠通過代理服務啓發回給某個UDP客戶端(因爲它登記了一個關於Socket UDP的通路映射)。所以爲了發送UDP數據,必須建立和保持這個TCP數據。RFC1928也提到,不能取得代理服務器的通道後就關閉TCP連接,否則代理服務器以爲UDP Socket通過代理的請求已經結束,不需要繼續保留UDP的對外Socket映射記錄,從而導致每發送一次UDP就要重新建立TCP連接協商UDP映射,增加不必要的麻煩。所以,我們需要保持UDP客戶端到到代理服務器的TCP連接持久,不必顯式關閉它。

  3、 UDP本地端口選定

  UDP大多數是同具體端口相關的,所以一定要在同代理服務器協商UDP映射時告知客戶端UDP的端口。一來將UDP同某個端口綁定,使得代理服務器接收UDP數據並轉發,二來也告訴了代理服務器將來在某個端口發出去後得到的反饋數據也按照線路返回給客戶的此端口。這一點很重要,筆者在此處犯了錯誤導致浪費了很多時間。

  4、 TCP/UDP連接二重性

  可以看到Socks5 的使用會佔用至少一個TCP連接,這樣導致代理服務器的負擔很重。所以在具體的應用時,需要考慮關於代理服務器的存在的負載問題。

  以上即關於UDP穿透Socks5代理的一點心得,希望能夠得到大家的指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章