用c#編寫socks代理服務器,大白話細述協議的最重要部分。

由於我是個粗人,是個菜鳥,只會講大白話,只想知道咱老百姓想聽的內容。

 

不知道爲什麼那些網文作者都說socks代理比http代理複雜,http代理和socks代理我都做了,明顯感覺http代理比socks代理要複雜很多,因爲http代理要自己解析http協議,這是我的http代理http://blog.csdn.net/laotse/archive/2010/09/24/5903651.aspx

而socks代理除了開頭那一點點外,其他就是什麼不管就轉發轉發就行了。

我做的那個http代理可謂很失敗,用着用着就cpu100%佔用了,應該就是解析http協議頭做的不好,出現死循環了或者別的,那麼多http頭實在不想再分析了,ccproxy我也sniffer了,結果是看到它也出現了許多錯誤的解析,ccproxy都多少年了,都解析不好,說明解析http協議確實不容易啊。

所以我很懷疑那些網文作者到底做沒做過http代理就信口開河說http代理比socks代理簡單。

 

socks代理裏面,有socks4 socks4a socks5,也有叫sock4 sock4a sock5的,都是一回事。

socks4 socks4a和socks5的tcp部分極其簡單。

這又出現一個怪現象了,網文中除了一個人說道udp比tcp複雜,其他的全都說udp簡單,只說tcp部分,把udp都一筆帶過了。

而我就明顯感到udp比tcp複雜很多。

 

socks的rfc是rfc1928和rfc1929,別看了,打個比方,某人看到一本書,當他看了目錄,準備往後翻看內容時,發現已經到底了,原來這不是“目錄”它就是內容,這就是rfc1928,話說rfc是幹什麼的啊,寫成這樣就算交活了啊,還有王法嗎,還有法律嗎。中文翻譯的就像是機器翻譯一樣,不是人類語言。所以造成網文上全都是連蒙加猜,沒有一個對的,這兩天我連續翻看那些網文,又用現成的帶socks的程序然後sniffer,算是知道了個大概,其實用程序編寫出來很簡單,關鍵就是rfc的協議說的不明不白的,所以造成本來很簡單的事情,弄得大家全都一知半解。

 

 

socks4和socks4a只代理tcp。

socks4

比如ie可以直接用socks代理,但是ie只能用sock4代理,即使我現在用最新的ie9仍然如此。比如ie要用sock4訪問百度,那麼ie就先用本地的dns把百度轉成ip比如202.108.22.5,然後向sock4服務器發送

04 01 00 50 CA 6C 16 05  41 64 6D 69 6E 69 73 74 72 61 74 6F 72 00

1、其中開頭04 01是固定的

2、00 50是ie要連接站點的端口號,這裏是80端口,十六進制就是00 50

3、CA 6C 16 05就是202.108.22.5的十六進制

4、41 64 6D 69 6E 69 73 74 72 61 74 6F 72是Administrator的acsii碼

5、最後一個00也是固定的

這就是socks4的固定格式04 01+端口2字節+ip4字節+id+00,其中的id,這裏是Administrator是可有也可以沒有,因爲我現在win7用的賬戶是Administrator所以ie就把賬號名給發過去了,我試了一下最新的火狐4.0,他發送MOZ,有的就直接省略掉了

04 01 00 50 CA 6C 16 05 00 所以發成這樣也行,隨便寫什麼都行,只要最後一個是0中間沒有0就行了。

如果代理服務器允許代理,比如驗證一下那個id,比如你想只允許id是administrator後,允許代理了,就發回8個字節

04 5A 00 50 CA 6C 16 05

看到了嗎,區別就是第二個字節是5A而且最後沒有00,那麼就是允許代理。其實根據協議5A這裏可以換其他的反饋字節,但是我們就不要管了,如果你做的代理程序不允許他用,那麼直接就在程序中斷開連接就行了。

如果允許連接了,然後就把客戶端的所有數據原封不動的發到遠程主機,把收到遠程主機的內容原封不動發回客戶端就行了,以下我就簡稱交換交換就行了。

 

socks4a

細心的人會發現一個問題,在sock4中,客戶端比如ie要用sock4代理訪問百度,ie會在本地dns解析出百度的ip202.108.22.5,而不是通過sock4代理的那個機器的dns解析的,這樣就會出現一個問題,比如你的那個機器是個受限制的網絡dns不給你解析域名,只讓你以ip方式訪問網絡,那麼怎麼辦呢?就沒辦法了對吧,你知道百度的ip沒用啊,網絡上都以域名形式存在的,其他的你都能知道嗎對吧。那麼ie爲什麼要在本地dns解析而不再sock4代理機器上解析呢?因爲sock4協議就這樣的,否則就不叫sock4協議了。

所以sock4a協議就是爲了彌補這個問題的,可以讓客戶把域名發到sock4a代理機器上,用這個代理機器上的dns解析出ip,這樣就克服了客戶端不方便解析域名的情況,而且有的網站比如google在本地解析和在代理服務器解析域名得到的ip是不一樣的,在代理服務器解析域名得到的ip更好,一般連接速度更快也更像就是從代理服務器發起的。

socks4a是這樣的,截取我用flashfxp通過socks4a代理登錄sourceforge的sftp

04 01 00 16 00 00 00 01  6C 61 6F 74 73 65 00 65 62 2E 73 6F 75 72 63  65 66 6F 72 67 65 2E 6E  65 74 00

1、04 01是固定的

2、00 16是端口22的十六進制

3、00 00 00 01是原本ip地址的地方,這裏前3個字節必須爲0,最後一個必須不能爲0,一般都是1

4、6C 61 6F 74 73 65是laotse的ascii,我設的flashfxp代理的命名,可以沒有,和sock4一樣

5、00 固定的,和sock4一樣,如果第四步的id沒有,這個00也不能省略

6、65 62 2E 73 6F 75 72 63 65 66 6F 72 67 65 2E 6E 65 74 就是域名web.sourceforge.net的ascii了

7、00 固定的。

這樣就可以看出,就是把sock4的ip那地方換成00 00 00 xx這樣的假ip,然後再sock4後面多了域名+00

這樣socks4a代理首先把web.sourceforge.net解析成216.34.181.70(D8 22 B5 46),並且和216.34.181.70連接上,再向客戶端發送

04 5A 00 16 D8 22 B5 46

和sock4一樣的,依然是04 5A+端口+IP共8字節。

然後就和sock4一樣就是轉發轉發了,如果不允許代理直接斷開就行了也不用發送什麼反饋了。

所以明顯socks4a比socks4要好,這樣避免了本地對域名解析帶來的問題,話說如果你要上些不和諧的站呢,總是用本地dns解析那些域名……是吧。所以能用socks4a就不要用socks4,但是杯具的是ie只能用sock4,ie9也如此,火狐也是。

 

看到了吧socks4和socks4a就是這麼簡單,簡單的不能再簡單了。

 

 

socks5

socks5代理和socks4 socks4a比,多了一個驗證功能和udp代理的功能。

socks5的tcp代理幾乎和socks4 socks4a一樣簡單,但是udp卻比較複雜一點,但是再複雜也沒有http代理那麼複雜。

首先說驗證,不管是要代理tcp還是udp,開始驗證都是要一個tcp連接的,而且驗證過程一樣,驗證完了才分tcp和udp的實際代理過程,有好幾種驗證,別管了,其他的都用不到太小衆化了,那些協議是什麼都沒聽說過,也沒處試驗去,信我的,就記住3種就行

如果一個客戶想通過socks5代理,那麼開始他會發送以下3種內容

05 01 00 共3字節,這種是要求匿名代理

05 01 02 共3字節,這種是要求以用戶名密碼方式驗證代理

05 02 00 02 共4字節,這種是要求以匿名或者用戶名密碼方式代理

 

如果socks5代理允許匿名那麼就返回05 00兩個字節,如果要求驗證就返回05 02兩個字節。

這裏匿名等一下,先說要求密碼驗證的,因爲要求驗證就比匿名多了一步而已,後頭是一樣的,所以先說密碼驗證。

當上面socks5返回05 02兩個字節後

客戶端發送01 06 6C 61 6F  74 73 65 06 36 36 36 38 38 38

1、01固定的

2、06這一個字節這是指明用戶名長度,說明後面緊跟的6個字節就是用戶名

3、6C 61 6F 74 73 65這就是那6個是用戶名,是laotse的ascii

4、又一個06共1個字節指明密碼長度,說明後面緊跟的6個字節就是密碼

5、36 36 36 38 38 38就是這6個是密碼,666888的ascii。

6、假如這後面還有字節,一律無視。

這時socks5代理就驗證了用戶名laotse密碼666888對不對啊,如果不對直接關閉連接就可不用反饋了。

如果這個用戶名和密碼通過了,可以進行代理,那麼就發送01 00給客戶端。那麼下面就和匿名是一樣的了,匿名就是省略了這一步而已。

這時無論匿名或者通過了密碼驗證的客戶端向socks5發送下列三種方式

先說tcp的

第一種

05 01 00 03 13 77  65 62 2E 73 6F 75 72 63  65 66 6F 72 67 65 2E 6E  65 74 00 16

1、05固定

2、01說明是tcp

3、00固定

4、03說明後面跟着的是域名而不是ip地址,由socks5服務器進行dns解析

5、13前面指明瞭是域名,那麼0x13(19字節)是域名字符長度

6、77 65 62 2E 73 6F 75 72 63 65 66 6F 72 67 65 2E 6E 65 74 就這19個是域名web.sourceforge.net的ascii。

7、00 16端口,即爲22端口。

第二種

05 01 00 01 CA 6C 16 05 00 50

1、05固定

2、01說明tcp

3、00固定

4、01說明是ip地址

5、CA 6C 16 05就是202.108.22.5了,百度ip

6、00 50端口,即爲80端口

看到了嗎,tcp的可以本地解析出ip來,只讓socks5代理去連,也可以發過域名去讓socks5去用它的dns去解析ip再連接

 

這時代理服務器收到了上面的請求後,如果是域名的,先解析出ip來連接,如果直接是ip的就用一個tcp連接去連接那個ip和端口,如果和遠程主機連接成功了,就向客戶發送什麼呢

05 00 00 01 C0 A8  00 08 16 CE共10個字節

無論上面兩種哪一種都是這樣

1、05 00 00 01固定的

2、後面8個字節可以全是00,也可以發送socks5服務器連接遠程主機用到的ip地址和端口,比如這裏C0 A8 00 08,就是192.168.0.8,這是我socks5服務器的ip地址,16 CE即5838端口,即是socks5服務器用5838端口去連接百度的80端口。也可以05 00 00 01 00 00 00 00 00 00,只告知客戶連接成功不告訴他細節,但是0不要省略。

 

後面就是在客戶和遠程主機之間轉發轉發啊的,是不是很容易啊,比http代理簡單太多了。

 

然後說udp的,udp的要複雜不少。首先要說下原理,udp和tcp不一樣,不是一個連接中一口氣下來的,上面說了不管tcp還是udp上面的那個tcp協商部分都是一樣的,而且如果是udp的話會佔用socks5代理一個tcp連接一個udp。

第三種udp的

客戶端如qq發送(仍在剛纔的tcp中發送)

05 03 00 01 00  00 00 00 E5 F0

1、05固定

2、03說明是要代理udp

3、00固定

4、01固定,只能制定後面跟着的是ip地址

5、00 00 00 00這裏可以由客戶端如qq發送客戶的ip,也可全是0,因爲這個ip地址沒用。

6、E5 F0最重要的一條,客戶端比如qq,向socks5代理說明它預備開放的udp端口,這裏是58864。

那麼socks5怎麼回答呢?如果不同意代理直接關閉連接就可以了不用反饋了。如果同意的話,socks5要這麼做,首先準備一個ip和一個udp端口,比如我用192.168.0.8這個ip上開放58865udp端口給客戶轉發用。然後返回

05 00 00 01 C0 A8  00 08 E5 F1

1、05 00 00 01固定

2、C0 A8 00 08預備開放udp端口開放給客戶的ip,這裏是192.168.0.8,如果多ip機器,那麼返回下面開放udp端口綁定的那個ip。

3、E5 F1返回給客戶說明預備開放哪個端口,這裏是58865。

 

好了tcp協商部分完成了,注意這個tcp連接一定不要關閉,要一直開着,雖然再也不會發送和接受數據了,但是要一直開着,如果這個連接一關,那麼客戶就認爲連接被斷開了,因爲這就是socks5協議,所以說socsk5轉發udp不但要佔用一個udp還要佔用一個tcp連接。麻煩吧。

 

上面那個tcp不要關,下面就是客戶和socks5的2個udp端口之間進行數據交換了,這裏udp麻煩的一方面又體現出來了,它就是無連接的,它不像tcp那樣因爲tcp協議部分就保證了數據的可靠性,這麼說吧比如tcp連接qq發送abcdef,這些數據太大了一次發送不完,那麼就會分片,那麼socks5可能會收到ab第二次c第三次def這樣的,雖然不知道能一次收多少,但是隻要連起來還是abcdef的順序,而且不會出現數據丟失而發送方不知道的情況。udp就不一樣,可能第一次就接到了def,第二次才接到a,bc可能直接就丟了還不知道,所以要保證udp數據的完整,不能靠udp協議這一層了,得自己手動指定,那麼在socks5裏就有一個udp分包的概念,就是在頭幾個字節指定這是1號包還是2號包,socks5程序必須自己弄個排序,比如第一次接到標記爲3號的包,那麼先存起來等着1號2號來了把3號放後面再發,所以說是很麻煩的,而且rfc也說了應用程序儘量不要弄這種分包,而且rfc說了,socks5程序可以選擇拒絕這種分包方式接到後直接丟棄而不通知客戶端,所以既然那麼麻煩,咱也不用去實現這用不大上的功能,因爲即使你這socks5程序實現了,對於應用程序比如qq來說還是不可靠的,而使用udp的應用程序在它應用程序本身就有個完整性和排序的功能,比如丟包了,qq之間自己就知道了,qq之間自己會去想辦法重發還是排序的什麼的,所以我們就不用去管分包了,讓應用程序自己去解決吧,我們只實現轉發不分包的那種就行了。

 

 

 既然只實現不分包的,那麼格式就固定了

客戶端qq的58864udp端口向socks5的58865udp端口發送什麼呢,仍然是ip+端口或者域名+端口方式

00 00 00 01 70 5F F0 3C  1F 40 +實體數據          

比如這個,00 00 00 01開頭,那麼後面4個就是ip地址70 5F F0 3C即112.95.240.60,1F 40 即端口8000,後面的全都是實體數據了。那麼socks5服務器就用58865udp端口向qq的服務器112.95.240.60的8000端口發送後面的實體數據而不要發送前面那些封裝內容,那麼會受到qq服務器返回58865udp端口的數據,返回的都是實體數據,因爲代理嘛,就像是socks5那臺機器在用qq一樣,所以收到的數據沒有前面的封裝都是實體數據。那麼socks5就要返回給客戶端,還不能直接返回,得包裝一下

00 00 00 01 70 5F F0 3C  1F 40 +收到遠程主機返回的數據

把這個返回給客戶端的58864udp端口

是不是前面包裝內容都是一樣的啊,是一樣的,因爲客戶端已經指明瞭ip,所以肯定是一樣的。

 

還一種是這樣域名的,qq的58864udp端口發送給socks5的58865udp端口
00 00 00 03 12 67 72 6F  75 70 63 6C 69 65 6E 74  2E 71 71 2E 63 6F 6D 23  29+實體數據

00 00 00 03開頭說明後面跟的是域名,緊跟着的12說明後面0x12(十進制18)字節就是域名,解出來就是groupclient.qq.com

後面23 29即端口9001。那麼socks5服務器就要先把groupclient.qq.com的ip給dns出來58.251.62.164(3A FB 3E A4 ),用58865up端口向58.251.62.164的9001udp發送後面的實體數據,返回來後和上面一樣向客戶qq的58864udp發送

00 00 00 01 3A FB 3E A4 23 29 +收到遠程主機返回的數據,03變01了,直接就ip+端口了

 

大家注意到了沒,客戶和socks5tcp協商後,socks5開放的udp端口,既和客戶開放的udp端口聯繫,也和需要連接的遠程主機之間聯繫,都用一個端口所以有點亂,這樣就得判斷了,如果發現像這個端口如58864udp發送數據的ip和udp端口,是之前協商的那個,就說明是客戶的數據,這時就要把客戶要發送的遠程主機的ip和端口記錄下來,比如上例的58.251.62.164的9001udp端口,如果發現是從58.251.62.164的9001udp發送過來的數據,那麼說明是遠程主機發回的,那麼需要轉發給協商好的客戶,還有一種情況,既不是客戶也不在遠程主機列表中的機器發過來的數據,就要丟棄,而且比如說客戶發過來一條數據是要發送給遠程A的a端口,那麼發送出去接收到返回給客戶,客戶又繼續發過來一條,這次要發給遠程B的b端口,那麼就要發出去接收返回給客戶,那麼這時遠程主機就要有個列表了,現在有2條記錄,只要接收到的udp在這2條中,就要轉發給客戶,如果客戶又要發給C的c,那麼列表就3條記錄了,那麼可能4條5條。

 

 

看出來了吧,socks5代理udp比起tcp來是很麻煩很麻煩的,不但要佔用一個tcp一直維持連接,而且還要手動搞這種列表,但是話說回來了,雖然麻煩,但是比起編寫http代理去解析http還是要容易多了。

 

socks5還有一種bind的tcp方式,說是ftp協議中有一種主動模式是一個tcp連上ftp服務器的21後,經過協商,服務器的某端口會反向主動連接客戶端的某端口,很早以前好像見過,現在這種模式基本沒用,ftp服務器和主機協商有什麼用啊,現在的機器要麼是在防火牆後,要麼是在局域網中,ftp服務器反向連接怎麼能連上客戶呢是吧,所以現在的ftp幾乎都用的是客戶主動連接ftp服務器的被動模式,socks5的bind就適用於那種老的主動模式,用處很小很小,所以咱不去考慮。

 

 

 ie9只能用sock4,4a和5不行

火狐4可以用sock4, 4a不行,可以用sock5,但是是個半殘,不支持用戶名和密碼驗證,而且sock5中我上面說了可以發域名過去讓sock5代理去dns解析,但是火狐4卻非要在本地解析域名,只用sock5的ip模式,無法達到徹底隱藏的目的,話說如果你想上某些不和諧站,如果被人發現你老是在本地dns解析那些域名……是吧。rfc管這叫dns泄露。

 

 

轉發轉發,怎麼實現客戶和遠程主機之間的轉發呢?

看我寫的這一篇,有代碼,很簡單!http://blog.csdn.net/laotse/archive/2010/09/10/5874778.aspx

 

 

超時怎麼做啊?

我覺得,無論4 4a 還是5,對於tcp的代理,如果發現無論遠程還是客戶只要5分鐘內沒有數據傳輸,就把遠程和客戶的這2條tcp連接斷開就行了,對於5的udp也是,如果發現udp端口5分鐘都沒有客戶的udp數據包發過來,就把這個udp端口關閉,把那個和客戶維持的tcp斷開。而不論4 4a 還是5,只要客戶主動斷開,那麼就把爲這個客戶開的一切資源全都關閉掉。不用擔心,一般程序都會定時發送維持性連接信息的,不會在那連着不收不發就那麼耗着不管了的,所以5分鐘都沒數據,就可以認爲已經斷開了,就關閉就行。

這個問題很嚴重,如果不做好不一會就會把服務器的端口全部佔滿,親身體會,其實早斷開了,但就是不釋放,好幾天都顯示ESTABLISHED,等到4294967295秒後纔會自動關閉,所以這個問題要慎重,保守一點好。

 

 

socks代理仍然是明文,就開頭幾個字節後面全是明文,並不能真正達到隱藏的目的。而且socks代理不支持加密連接。所以我是這樣做的。 肉雞讀取到網絡上的某個固定網站上面的某個ip,就連接到我了,是以rsa+aes加密連接的,所以在我的某端口和我的肉雞某端口之間建立了一條加密隧道,ie等程序通過這個加密隧道來到肉雞上面,連接到那裏的socks代理服務器,通過socks代理連接網絡服務器。這樣連接過程是絕對安全的,而且當初我編寫反向連接服務器中的aes設的加密位數和密鑰初始化向量都是用最大號的最變態的那種。以前我是把http代理放肉雞上通過這個加密隧道上網,但是由於http代理只能http協議瀏覽網頁用,而且我一開始說了,那個http代理我做的比較失敗,經常不明原因就死循環了。現在好了,全都能用了, 應用程序願sock4就sock4,願sock5就sock5,各取所需,多線程同時連接,互不干擾,而且全都是通過反連過來的加密隧道,而且這種不用分析tcp之上高層協議只轉發的速度快,不會因爲解析協議的邏輯錯誤而出現死循環的情況,試了兩天很好,cpu佔用率基本爲0左右,內存16兆左右。

 

 

對了忘記說明重要的一點了,以上我只是用qq舉例說明代理udp的過程,我是不用qq的,有的同志看到我的文章後和我發郵件聯繫就一句話“希望交流某某某 qq:xxxxxx",我是不用qq的,qq我一年也開不了2、3次,只是我在做這個socks程序,纔開了開qq進行測試用的,我聊天都是泡論壇。有想交流的,要麼發郵件,最好能光臨我的網站發帖。

發佈了31 篇原創文章 · 獲贊 10 · 訪問量 65萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章