goproxy和msocks簡介

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

goproxy是我個人寫的,和shadowsocks同類的軟體。當然,在設計之初我完全不知道shadowsocks的存在,goproxy的最初目標也不是成為shadowsocks的同類。只是我一直無法實現一個可靠的,能夠達成目標的系統。最後想,那這樣吧,我找一個跳一跳能夠夠到的蘋果。大幅簡化的結果就是goproxy——後來我才知道shadowsocks。

shadowsocks的基本原理

shadowsocks的基本概念,就是利用某種不同於SSL的協議,將本地的socks資料流轉寄到遠程。這個協議,在預設版本中是一個凱撒變換,後來有了aes等密碼編譯演算法。goproxy也採用了類似的做法,同樣支援aes等密碼編譯演算法。在每次串連時,用戶端先用加密通道串連伺服器端,然後完成整個串連通路。這樣的設計魯棒性相當好,但是作為代價的,也有不少缺陷。

首先,goproxy和shadowsocks不約而同的採用了自己的協議,而非將socks5透明的轉寄到遠端伺服器端。為什嗎?因為socksv5協議中,握手過程是三次互動。客戶發送握手包,伺服器響應允許的握手驗證方法。客戶發送驗證報文,伺服器端返回是否成功,客戶發送要串連的目標,伺服器端返回是否成功。細節我記得不是很清楚,但是2-3次往返是必須的。

這種工作機制需要client -> proxy-client -> proxy-server -> server的一個鏈條,本身就比直連多了兩次TCP握手。加上上述的往返過程,更加耗時。而且這個消耗在每次建立連結時都要來一次,而HTTP是一種短連線協定——這就更加無法容忍了。因此改用自有協議,一次互動完成握手,就會更加快速。

更根本的原因在於,這兩個系統都需要越過IDS,而三次互動的報文大小是幾乎固定的——就算加密也無法改變報文大小。不但大小一樣,而且由於使用者名稱密碼相同,起始加密過程和IV一致,因此採用socks協議的話,每個連結開始都有相同的來返資料。

我不知道shadowsocks怎麼處理的這個問題。qsocks協議(msocks)的前身規定,每次握手時用戶端提供一組IV,然後發送一個頭部變長的字串(256字元以內),在遠程丟棄同樣長度的隨機字元。經過這樣的處理,每次連結時的報文長度和內容序列都不一樣,增加了破譯難度。至於多出來的幾十個位元組,和驗證報文在一個報文內,開銷相比一次RTO幾乎可以忽略不計。

但是還是有一點無法避免的問題。如果你看到某個伺服器上有一個連接埠,頻繁的被一個或多個IP連結。每個連結都不長,每次都是用戶端吐一堆資料,伺服器返回一堆,然後關閉連結。儘管協議無法破解,但是基本可以肯定這就是shadowsocks。根據這個特性,可以有效阻擋服務——這也是我最近碰到的問題。

而且每個連結都需要驗證和TCP握手太慢了。

msocks的改進

所以,我參考SPDY協議,做了msocks。msocks的核心思路和qsocks很類似,主要修改是以下兩點:

  1. 使用一個可靠連結(這裡是經過加密的TCP),在這個連結裡面封裝多對傳輸。
  2. 每個連結只要一次驗證。

這樣做,首先減少了一次TCP握手和一次身分識別驗證,工作速度更加快。其次多個傳輸疊加在一個流裡面,流特徵更加變化莫測。最後,無論是伺服器端還是用戶端的開銷都小了很多。

當然,這也帶來不少問題。例如TCP原本的擁塞控制視窗是為了一對傳輸序列設計的。當很多傳輸序列在一對TCP上傳遞的時候,丟報文造成的影響會作用作用在全體傳輸序列上。包括丟了一個報文重傳的時候,所有序列都必須阻塞。還有基礎的TCP被施加了丟包,導致全體序列共用5k頻寬。當然,經過評估後,我覺得這些問題比頻繁握手更加輕,所以就設計了msocks協議。

協議設計的時候,有幾個細節問題。

多對複用

我採用了一個map,來記錄某個id是否對應到了一個控制結構。這個映射只能被用戶端更改,並且有個專門的函數負責尋找閒置id,每次產生的id都是遞增的,如果碰到最大值則繞回。

id的大小是16位,足夠容納65536對同時連結。其實不修改核心的話,500對代理就會導致too many files。

實際上一般到id達到400後,單一的tcp就斷線重連了。目前我還沒見過上千的數字呢。

串連狀態

串連一般情況下可以看到5種狀態,串連請求發送,串連請求接收,串連建立,主動關閉串連中,被動關閉串連中。

當用戶端請求代理串連一個遠程伺服器時,進入串連請求發送。代理遠程端接受後在串連目標伺服器的過程中,進入串連請求接收。當成功後,雙方進入串連建立。

當關閉時,主動發起關閉一端進入主動關閉,另一端進入被動關閉。當被動關閉端調用close,或者主動關閉端收到對方關閉,整個連結就銷毀。

由於tcp是可靠傳輸,因此三向交握和四次關閉都是不必須的。

簡單吧。

擁塞控制

TCP原本是帶有擁塞控制的——藉助SSN雙序列和視窗機制。但是在多工時候,我們需要自行控制擁塞——而且不能採用會和機制。會和會導致後續已經到達的其他連結的報文被一個沒人接收的報文阻擋。所以必須採用帶擁塞控制的緩衝隊列機制。

不過幸好,TCP本身是可靠傳輸協議,所以我不用擔心丟包重發之類的問題。我需要做的,就是把對方讀取的位元組數傳遞迴來,減在控制器上,即可。

不過,我沒有做對應於silly window syndrome的最佳化,在每次讀取小資料量後,這個讀取造成的window擴張都會被傳回。當然,這麼設計是有原因的。我預設採用了8K的buffer進行fd間拷貝,所以一般碰不到SWS。

為瞭解決tcp連結複用造成的單串連頻寬問題,我強烈的建議你做以下的設定:

net.ipv4.tcp_congestion_control = htcpnet.core.rmem_default = 2621440net.core.rmem_max = 16777216net.core.wmem_default = 655360net.core.wmem_max = 16777216net.ipv4.tcp_rmem = 4096    2621440 16777216net.ipv4.tcp_wmem = 4096    655360  16777216

ip選擇演算法和DNS

在goproxy中,我沿用了一個做法。通過DNS獲得請求的目標IP,和中國IP範圍核對。如果在國內則直接存取,否則透過代理。這個方法能夠極快的加速訪問,而且幾乎不依賴於需要更新的列表(中國IP列表相對來說固定)。

問題是DNS解析過程。msocks內建了DNS能力,可以協助做DNS。但是實踐下來發現這樣做效果並不很好。而原本是採用直接DNS,丟棄特定的報文。這樣可以過濾防火牆汙染。

原因很簡單。原本的模式會讓DNS伺服器感知到查詢者位於中國,於是給出中國可以訪問的最快地址。而新的模式則會將DNS要求者搬到美國——這無故加重了代理的負擔。例如www.qq.com,原本只需要請求得到一台深圳的伺服器即可,現在則需要讓DNS繞出去,再回來。如果不幸,QQ有一台位於美國的伺服器,那麼我的訪問都會通過這台伺服器——這可比深圳的伺服器慢多了。

地址

抱歉剛剛忘記寫地址了:

goproxy

debian/ubuntu安裝包:amd64 i386

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.