UPNP自動端口映射的實現與路由器UPNP相關資料

UPNP的全稱是 Universal plug-and-play( 通用即插即用).UPnP 是針對智能家電、無線設備以及各種外觀尺寸的個人電腦的普遍對等(peer-to-peer)網絡連接而設計的一種架構。它旨在爲家庭、小型企業、公共場 所中或連接到互聯網的ad-hoc 網或未管理網絡提供易於使用、靈活且基於標準的連接。(引自這裏.)

  我們這裏用到的自動端口映射只是UPNP的一個小應用。按照UPNP的相關規範,UPNP網絡的第0步是尋址(獲得一個IP地址,在我要解決的問題中這不是一個問題。)       
    第1步是發現,控制點在網上搜索感興趣的設備,而設備向網絡中的控制點宣告其服務。對於自動端口映射來說就是發現帶UPNP功能的路由器。

 發現這個過程主要有兩步。第一,使用數據報套接字向239.255.255.250:1900,發送一條多播請求,格式如下

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN:"ssdp:discover"
MX:3
ST:UPnP:rootdevice

      這個多播請求的含義如下:M-SEARCH SSDP協議定義的搜索請求方法。HOST必須是這個多播地址。MAN的值也必須是"ssdp:discover" 不可少了雙引號。MX的含義是最長等待時間,可以自己設置。ST表示search target 搜索目標。我們在這裏用找根設備。另外在編程中我們要在每一行後面加上"rn" 表示換行。(詳見源碼 UPNPNAT.discovery()).

      第二步,如果你的網絡存在一個UPNP設備的話,爲了被找到,設備必須向發送查找請求的多播通道的源 IP 地址與端口發送響應信息。所以你可以從239.255.255.250:1900這個地址接收到響應消息。類似下面的消息。

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=100
DATE: Sun, 15 Jan 2006 06:51:02 GMT
EXT:
LOCATION: http://192.168.14.1:1900/igd.xml
SERVER: TP-LINK Wireless Router WR541G/5, UPnP/1.0
ST: upnp:rootdevice
USN:uuid:upnp-InternetGatewayDevice-192168141678900001::upnp:rootdevice

      接下來我們要從裏面獲得我們要的消息。首先,我們必須找到" 200 OK ",說明沒有錯誤發生,否則一切免談。接着,我們要找到LOCATION項,獲得設備描述URL。(程序中的處理歸根到底就是一個子字符的查找。)

       到這裏,我們的第一步“發現”完成。


   第2步是描述。在第1步中我們往往能獲得一個設備的描述URL,在第2步中我們要通過一個URL,下載一個XML文件。並從中找到有關設備的類型,服務類型,控制URL,事件觸發URL等。

我們同樣分兩步進行,首先下載設備描述文件。(請看源碼中UPNPNAT::get_description()函數)
    1.解析描述文件的URL,獲得主機(host)、端口(port)、路徑(path).(parseUrl函數) 
    2.連接到host:port (tcp_connect 函數)
    3.構造類似

                                                GET path HTTP/1.1
                                                Host: host:port
的信息(第二行下要一個空行),並通過剛纔的TCP 套接字,發送到路由器。(sprintf ,send 函數).
    4.接收數據,我使用flag爲 MSG_WAITALL的recv函數,函數一直阻塞直到數據全部讀完。 數據最終保存在std::string description_info中。
    我想通過瀏覽器下載這個文件的過程是類似的吧。
 
    然後,解析這個XML文件。(請看源碼 中UPNPNAT:: parser_description()函數)
我們找到"root"的"deviceType"是"urn:schemas-upnp-org:device:InternetGatewayDevice:1"的"device" childNode ,獲得這個"device"的"deviceList",記爲A。
    找到A的"deviceType"是"urn:schemas-upnp-org:device:WANDevice:1"的"device" childNode ,獲得這個"device"的"deviceList",記爲B。
    找到B的"deviceType"是"urn:schemas-upnp-org:device:WANConnectionDevice:1"的"device" childNode ,獲得這個"device"的"serviceList",記爲C。
    找到C的"serviceType"是"urn:schemas-upnp-org:service:WANIPConnection:1" 或"urn:schemas-upnp-org:service:WANPPPConnection:1" 的"service"  childNode ,記爲D.
    獲得D的"controlUrl"保存在std::string control_url中。
    但是這裏獲得control_url一般爲相對URL,所以要從"root"下面,找到"URLBase"的值,(如果是空,則用describe_url的"htpp://xxx.xxx.xxx.xxx:xxxx"部分代替.)
    最後在相對的control_url前加上URLBasr 獲得完整的control_url.
 
    至此,第二步“獲得控制URL”完成。
                                      

    第3步是控制。通過第2步獲得的控制URL,通過向其發送控制消息(同樣用XML描述)來實現某些功能。對於自動端口映射來說就是查看、增加、刪除等。

在這裏我先把各種控制信息的格式說明一下。(下面的rn都是表示換行,我輸入不了反斜槓。)

  • 增加端口映射。 "AddPortMapping"
  • "<NewRemoteHost></NewRemoteHost>rn"                              "<NewExternalPort>ExternalPort</NewExternalPort>rn"                               "<NewProtocol>Protocol</NewProtocol>rn"                                      "<NewInternalPort>InternalPort</NewInternalPort>n"
    "<NewInternalClient>InternalClient</NewInternalClient>rn"                 
    "<NewEnabled>1</NewEnabled>rn"
    "<NewPortMappingDescription>PortMappingDescription"       "</NewPortMappingDescription>rn"       
    "<NewLeaseDuration>LeaseDuration</NewLeaseDuration>rn"
  • 刪除端口映射 "DeletePortMapping"
  • "<NewRemoteHost></NewRemoteHost>rn" "<NewExternalPort>ExternalPort</NewExternalPort>rn"   "<NewProtocol>Protocol</NewProtocol>rn"
  • 獲得端口映射信息 "GetGenericPortMappingEntry"
  • "<NewPortMappingIndex>PortMappingIndex</NewPortMappingIndex>"  "<NewRemoteHost></NewRemoteHost>rn"   "<NewExternalPort></NewExternalPort>rn" "<NewProtocol></NewProtocol>rn"     "<NewInternalPort></NewInternalPort>rn" "<NewInternalClient></NewInternalClient>rn" "<NewEnabled>1</NewEnabled>rn"  "<NewPortMappingDescription>"                                          "</NewPortMappingDescription>rn"            "<NewLeaseDuration></NewLeaseDuration>rn"

     其中斜體部分需要在編程是填入的。ExternalPort 外部端口。InternalPort內部端口。這 兩者一般就填映射的端口。Protocal 填TCP或UDP。InterClient 一般就是本地IP地址。PortMappingDescription 填寫端口映射的描述,比如什麼程序建立了這個端口。LeaseDuration 是映射的持續時間,用0表示不永久。PortMappingIndex 是端口映射索引,路由上第幾個映射。

     我們再來看下面這個XML文檔結構。

     "<?xml version="1.0" encoding="utf-8"?>rn" 
     "<s:Envelope xmlns:s="
     ""http://schemas.xmlsoap.org/soap/envelope/" "
     "s:encodingStyle="                                   
     ""http://schemas.xmlsoap.org/soap/encoding/">rn"
     "<s:Body>rn" 
     "<u:actionName xmlns:u="serviceType">rn"
     "actionParams</u:actionName>rn" 
     "</s:Body>rn"
     "</s:Envelope>rn"

     我們在actionName 處填入"AddPortMapping" "DeletePortMapping" "GetGenericPortMappingEntry"。serviceType 處填入設備的服務類型。"urn:schemas-upnp-org:service:WANIPConnection:1"或"urn:schemas-upnp-org:service:WANPPPConnection:1"。actionParams 填入上面的各種控制信息。

    最後在前面加上HTTP頭。

    "POST path HTTP/1.1rn"
    "HOST: host:portrn"      
    "SOAPACTION:"serviceType#actionName"rn"  
    "CONTENT-TYPE: text/xml ; charset="utf-8"rn"
    "Content-Length: contentLength rnrn"

    path host port 意思很明顯。contentLength面那個XML文檔的長度。

  然後連接到host:port,發送到整個信息即可完成控制            

    第4步事件觸發和第5步展示在自動端口映射沒用用到。

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