upnp

自己動手實現UPnP進行端口映射的經過

    從接到任務,到實現了 UPnP 在家用路由器上進行端口映射的時間總共花費了 1個半月,下面大概講講從資料蒐集到設計實現的經過,好給有同樣需求的人一些線索(不論及具體實現和代碼)。
    所謂 UPnP ,就是“通用的即插即用” ,注意是通用的,雖然很容易和 Windows 的即插即用混淆,但這肯定不是微軟的專利!現在大部分的路由器都支持這個功能,只是默認情況下沒有打開而已(基於安全考慮)。請管理員手動打開這個支持選項。

    這是用來幹什麼用的?如果我們要寫 P2P 軟件,那就用的着了,電騾不是有所謂的 LowID 和 highID 嗎? 爲了提高自己的共享能力(我爲別人共享,別人也爲我共享),我們(軟件)要使用公網 IP 地址監聽和建立連接,但是我們(軟件)不是路由器,如何監聽? 只好請路由器幫我們做一個端口映射,然後我們(軟件)在內網監聽,效果跟在公網上監聽一樣,也就是所謂 電騾的 HighID 了。 現在越來越多的用戶都是內網用戶的上網形式(NAT),如網吧。能夠把自己的 LowID 提升爲 HighID ,那麼肯定會有更多的備選數據源啊,這樣下載就被加速了!
不說那麼多廢話了,如何開始?所有資料都是在http://upnp.org/ 上,着重看 《Internet Gateway Device (IGD) V 1.0》文檔就行,其他都不是我們所關注的。這些文檔的打包裏,實際上對我真正有用的是一個叫做 《UPnP_IGD_WANIPConnection 1.0.pdf》 的文檔,其它的內容實在太多了,我偷懶都沒看。不過就算這篇文檔也不用急着看,先下載保存好,以後會用的到的。
後續的開發需要對報文抓包分析的,建議對自己用的順手的網絡報文分析工具先熟悉一下,我用的是 wireShark (前身是 Ethereal ),這個很重要。
碰 巧是,我得知,原來微軟有那麼一個 COM 組件是支持 UPnP 功能的,當時一個想法就是想直接用微軟的那個 API 實現了功能就是。上網搜,打開 MSDN 搜,終於給我發現,原來 Platform 2003 SDK 裏面有個例子,是專門用來執行 upnp 功能的,雖然不是直接用來做 端口映射的,但已經非常具備參考意義了。 下面那個就是我安裝 Paltform 2003 後的 Sample 目錄:  "D:/ProgramFiles/PaltformSDK/Samples/netds/upnp/GenericUCP" 請調整好您的 VC6 ,加載這個工程吧。
    既然編譯好了這個工程,那就要找一個試驗的環境了,我在網絡里加了一個家庭網關(小型路由器),並打開了它的 uPnP 功能,使用該工具體驗了一下如何找到一個 uPnP 設備(及指定service),並獲取它的狀態參數,向它發出 action 指令! 這時候,之前讓下載的 .pdf 文檔終於排上用場了。打開他,發現他裏面有一些參數(STATE VARIABLES)可以 Get 、 也有某些 action 可以執行(當然要帶上指定參數),那篇 pdf 文檔裏面都說的非常清楚。不說那麼多,先執行最簡單的 action | (GetExternalIPAddress),參數全爲空就行了。
經過測試,拋開STATE VARIABLES 不用(因爲要向網關注冊我所監聽的 event 地址的,而 action 則不用),只用 action 實現了自動端口映射,要用到的 action 有如下列幾個(參數不列出了,可以查文檔得知):
 ● GetExternalIPAddress
 ● GetGenericPortMappingEntry
 ● AddPortMapping
 ● DeletePortMapping

    好了,這幾個足已。這就是 uPnP 模塊的雛形了,不過這還不能直接拿來用的。把裏面能抄的東西都搬到自己的終端軟件當中,果然在簡單組網的環境裏還非常好用,不過,拿到廣州電信研究院進行 複雜組網測試的時候,災難發生了,網絡裏竟然有超過一個小型網關!最後,軟件竟然在別人的小型網關裏建立端口映射! 完蛋了,怎麼會這樣呢? 抓包分析,錯誤定位,這樣一步一步下來,也終於搞清楚了 uPnP 的整個交互流程,也萌生了自己實現 uPnP 功能模塊的念頭,實際上就是特定格式的 xml 交互嘛(http方式)。進行了完備的抓包分析之後,搞清楚所執行的 action 的特定 xml 命令的格式。我重新開始了編程,一個星期後重寫模塊完畢,拋棄了微軟那套操作 uPnP 的 API 以及煩人的頭文件包含,並且也不用擔心 Win2000 下不支持該 COM 組件了。
下面簡單講講整個 uPnP 的交互流程,如果有興趣的就姑且作爲一個開發過程的線索吧,具體報文內容請自行抓包GenericUCP 例程和家庭網關的交互則一目瞭然 :

  ① 一開始,程序隨便綁定一個 UDP 端口,向組播地址 "239.255.255.250:1900" 發送探測報文(查找的設備類型是WANConnectionDevice:1 ), 並在超時時間內 recvfrom( ) 等待迴應報文。如果網絡上的設備有滿足搜索條件的,都會直接回應 UDP 報文到該 socket 處。
  // -----------------  UDP 交互結束  -----------------------

  ② 如果迴應報文是正確的,那麼從中應該可以看到對端 uPnP 設備監聽的 Location 了, 一個 http 地址,向這個 http 地址發出 TCP 的 http GET 請求,得到一個 xml 說明頁 (可直接用 Iexplore.exe 打開來看),在裏面確認到有沒有 "WANConnectionDevice:1" 這個子設備,其下應該會有 "WANIPConnection:1" 的這個 service,把其中的 <controlURL> 取出來就行了。

  ③ 在取得 <controlURL> 以後,要做的事情就是向這個 http 地址發送 POST 請求,帶上相應 action 的控制命令 xml 就行了,具體可以用GenericUCP 做一遍,然後抓包後原樣照抄並做一些參數替換即可。

  - ④ – 可選的,如果想從 uPnP 設備那裏獲得一些參數變化的通告(比方有第三者新增或者刪掉了某端口映射,引起 "PortMappingNumberOfEntries" 參數的變化),可以向(從 Location 處 GET 回來的說明頁中的) <eventSubURL> 發出註冊請求,說明自己正在監聽的 http 地址,這樣的話最新參數變化後 uPnP 設備都會主動發到這個地址來,不過別忘了在程序退出前取消掉註冊哦。(爲了減少代碼量,我沒做這方面的支持)。 

 

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