找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 8994|回复: 4

P2P端口映射 UPnP功能和使用详解

[复制链接]
发表于 2007-12-14 00:00:59 | 显示全部楼层 |阅读模式
在网上看了很多关于如何打开UPnP功能的文章,发现竟然没有一篇文章能把整个UPnP的设置过程介绍全的,都是只讲到一部分。所以决定写篇文章,至少把设置UPnP的整体思路理一下,因为涉及到不同的操作系统以及不同型号的ADSL Modem,所以此文也不可能面面俱到,但至少提供一个较为完整的思路。因水平有限,不到之处还请高手指点。     一、UPnP的概念:
    以下是微软官方网站对UPnP的解释:
    问:什么是 UPnP?
    答:通用即插即用 (UPnP) 是一种用于 PC 机和智能设备(或仪器)的常见对等网络连接的体系结构,尤其是在家庭中。UPnP 以 Internet 标准和技术(例如 TCP/IP、HTTP 和 XML)为基础,使这样的设备彼此可自动连接和协同工作,从而使网络(尤其是家庭网络)对更多的人成为可能。
    问:UPnP 对消费者意味着什么?
    答:简单、更多选择和更新颖的体验。包含通用即插即用技术的网络产品只需实际连到网络上,即可开始正常工作。实际上,UPnP 可以和任何网络媒体技术(有线或无线)协同使用。举例来说,这包括:Category 5 以太网电缆、Wi-Fi 或 802.11B 无线网络、IEEE 1394("Firewire")、电话线网络或电源线网络。当这些设备与 PC 互连时,用户即可充分利用各种具有创新性的服务和应用程序。
    以下是BC官方网站对UPnP的解释:
    UPnP(Universal Plug and Play),通用即插即用,是一组协议的统称,不能简单理解为UPnP=“自动端口映射”。在BitComet下载中,UPnP包含了2层意思:
    1、对于一台内网电脑,BitComet的UPnP功能可以使网关或路由器的NAT模块做自动端口映射,将BitComet监听的端口从网关或路由器映射到内网电脑上。
    2、网关或路由器的网络防火墙模块开始对Internet上其他电脑开放这个端口。
    我倒是觉得微软的解释过于理论化,适合写入牛津大词典;而BC官方网站的解释过于含蓄晦涩。其实对于一般的使用者来讲,简单的把UPnP理解为自动端口映射就可以了。它就是一种基于TCP/IP协议的,针对设备彼此间的通讯而制订的新的Internet协议,目的就是希望未来所有联入Internet中的设备能够不受网关阻碍的相互通信。
    二、哪些用户需要用UPnP功能?
    1、只有在需要使用一些支持UPnP功能的P2P软件的时候,如BT、电骡eMule、MSN等,我们才需要考虑UPnP这个东东。如果你根本就不用这些软件,仅仅是上网浏览的话,下文就没必要看了;
    2、如果你需要使用这些P2P软件,但你是外网用户,那么下文也可以不看了,因为你不需要做什么UPnP就可以正常使用这些P2P软件了;
    3、如果你是内网用户,但你已经手动为这些P2P软件进行了端口映射,如在使用BC下载时,在“用户列表”中已经看到“远程”,或者是使用电骡eMule连接服务器成功后,已经显示为高ID,那么下文也可以不看了。
    但需要注意的是,手动做的端口映射只是针对某个P2P软件起作用,如果再使用新的P2P软件的话,仍然需要针对新的P2P软件做相应的端口映射才可以;
    4、如果你是内网用户,需要使用这些P2P软件,而且并未进行手动端口映射,比如在使用BC进行下载时,“用户列表”中只有“本地”而没有“远程”,在使用电骡eMule的时候,显示的也是低ID,那么此时我们才需要考虑端口映射的问题!
    这时我们可以有两种选择:
    1、进行手动端口映射。
    2、打开UPnP功能,进行自动端口映射,也就是我们下面所讲的内容;
    小结:UPnP自动端口映射的目的:
    以BC为例,手动端口映射和自动UPnP端口映射都是为了获得远程连接,因此凡是在“用户列表”里看见了“远程”的用户,都不需要进行端口映射或是UPnP!UPnP和端口映射只需要取其一,他们相当于达到目的的2种不同的方法而已。
    因此如果你的系统或者硬件不支持UPnP功能,大可不必伤心,按照第3点的链接文章中的方法手动做端口映射就可以了,效果是一样的;
    三、实现UPnP必须满足哪些条件:
    必须同时满足3个条件:
    1、Modem必须支持UPnP功能:是否具备此功能可查阅说明书或者直接咨询厂家。一般来讲,Modem还必须同时支持路由功能,除非你配备了单独的路由器;
    2、操作系统的支持:文章开头提到的那篇介绍UPnP的文章中,提到只有windows xp系统才支持UPnP功能,但微软的官方网站声称从Windows Me开始就已经支持UPnP功能了。但Windows Me这个操作系统我也没有用过,大家可以自行测试;
    3、软件必须支持UPnP功能:如BC、电骡eMule、MSN等软件都支持UPnP功能;
    顺便提一下,目前的几款视讯聊天软件各有其特殊性,象Netmeeting、QQ等就不支持UPnP功能,文章的最后会详细阐述一下这几款视频聊天软件的特殊性,此处不深入探讨,一笔带过;
    注意:以上3个条件必须同时满足,缺一不可,否则你只能考虑进行手动端口映射了;
    四、如何打开UPnP功能
    如果已经满足了上述的3个条件,那么我们就一步一步的讲解一下到底如何才能打开UPnP功能;
    1、在Modem中打开UPnP功能。
    不同型号的Modem设置界面和方法略有不同,如有些是在下拉菜单中选择Enable,但基本的原理都是一样的;
    有些文章提到,此时要把所有桥接的eoa连接都删除掉,我倒是认为大可不必如此。因为目前只有你的PPPOE连接是有效的,其它的几个eoa所对应的VPI和VCI根本就是无效的,所以没有必要删除。但是否有些型号的Modem会比较特殊也很难讲,因此建议此时暂不删除,把全部设置进行完后,如果还是没有打开UPnP,再尝试把eoa删除掉;
    当然,设置完后,一定要保存并重启Modem;
    2、在操作系统中打开UPnP功能:
    如果你使用的是XP SP2系统,则首先进入:控制面板->添加或删除程序->添加/删除windows组件中,在“网络服务”中勾选“UPnP用户界面”。
    确定后,系统会自动安装相应的组件,可能会提示你插入安装光盘,总之按照提示操作完成即可;
    接着打开Windows自带的防火墙,在“例外”选项卡中勾选“UPnP框架”。
    其实有个更加简单的方法可以同时完成以上两步:双击桌面上的网上邻居(注意是鼠标左键双击,不是右键查看属性),然后点击“显示联网的UPnP设备的图标”,系统会自动安装UPnP组件以及在防火墙中打开UPnP框架,实际上就是一次性完成上面两步的工作;
    如果你使用的是XP SP1系统,那么在“windows组件”中显示的是“通用即插即用”,而不是“UPnP用户界面”,选择此项即可。
    而且XP SP1系统的防火墙并没有UPnP框架的选项,需要手动进行端口添加,另一教程对此做了阐述,现引用过来:
    「请在防火墙设置中,点“高级”,然后自行添加如下两个端口:TCP端口类型,端口号为:2869,UDP端口类型,端口号为:1900 .由于你使用了NAT网关,所以你应该设置的是您连接到该网关的网卡的防火墙。而且网关内部均为内网,所以开启这两个端口,不会对系统造成安全隐患(除非你的NAT网关被绕过,否则外部连接无法检测到该端口)。」
    以上的防火墙设置只是针对windows自带的防火墙,如果你安装了其它的防火墙,必须在该防火墙中打开UPnP框架;
    3、在windows中打开相应的UPnP服务:
    进入“控制面板->管理工具->服务”,找到SSDP Discovery Service和Universal Plug and Play Device Host两项服务。
    右击相应的服务项,选择属性,启动这两项服务。
    做完以上工作后,如果操作正确,我们就可以在“网络连接”中看到多了一项网关,这表明添加UPnP已经成功;
    4、打开P2P软件中的UPnP功能:
    以BC和电骡eMule为例,相应的设置选项。
    到此为止,我们打开UPnP的工作才真正结束。
    以BC为例,成功添加UPnP功能后,在“全局日志”中我们会看到类似下面的几行:
    Windows XP UPnP Status: Found WAN Connection Device[Linksys Inc.] [http://www.linksys.com/] Windows XP UPnP Status: WAN IP: 218.30.*.* Windows XP UPnP Status: Port Mapping Existed!
    此时我们用BC进行下载,如果用户列表中有“远程”,或者用电骡eMule连接服务器后显示为高ID,那么就大功告成了!
    五、对几款视频聊天软件的简单对比说明:
    目前常用的视频聊天软件主要有MSN、Netmeeting和QQ等,这3款软件中只有MSN支持UPnP功能,而且发现在MSN的选项中并没有设置UPnP功能的选项,也就是说MSN始终是默认打开UPnP功能的。另外,MSN似乎也没有象BC或者电骡eMule那样提供可以手动进行端口映射的端口号,因此只要没有打开UPnP功能,MSN的功能就会受限,比如不能进行语音通信等;
    而Netmeeting虽然不支持UPnP功能,但是却提供了可以进行手动进行端口映射的端口号,如果你是内网用户,只要手动进行1503和1720两个端口的映射即可正常使用所有音视频功能;
    QQ是用UDP的方式,通过UDP服务器来实现音视频以及文件的传输,跟UPnP没有什么关系,所以无论内网还是外网,使用QQ都畅行无阻,只是传输的速率要慢些;
    说实话,本人平时也不常上网聊天,所以对这些聊天软件了解也不深,如果有说得不对的地方,欢迎高手指点。
 楼主| 发表于 2007-12-14 00:01:26 | 显示全部楼层
  1. UPNP.h
  2. #ifndef MYUPNP_H_
  3. #pragma once
  4. typedef unsigned long ulong;
  5. class MyUPnP
  6. {
  7. public:
  8. typedef enum{
  9.   UNAT_OK,      // Successfull
  10.   UNAT_ERROR,      // Error, use GetLastError() to get an error description
  11.   UNAT_NOT_OWNED_PORTMAPPING,  // Error, you are trying to remove a port mapping not owned by this class
  12.   UNAT_EXTERNAL_PORT_IN_USE,  // Error, you are trying to add a port mapping with an external port in use
  13.   UNAT_NOT_IN_LAN     // Error, you aren't in a LAN -> no router or firewall
  14. } UPNPNAT_RETURN;
  15. typedef enum{
  16.   UNAT_TCP,      // TCP Protocol
  17.   UNAT_UDP      // UDP Protocol
  18. } UPNPNAT_PROTOCOL;
  19. typedef struct{
  20.   WORD internalPort;    // Port mapping internal port
  21.   WORD externalPort;    // Port mapping external port
  22.   UPNPNAT_PROTOCOL protocol;  // Protocol-> TCP (UPNPNAT_PROTOCOL:UNAT_TCP) || UDP (UPNPNAT_PROTOCOL:UNAT_UDP)
  23.   CString description;   // Port mapping description
  24. } UPNPNAT_MAPPING;
  25. MyUPnP();
  26. ~MyUPnP();
  27. UPNPNAT_RETURN AddNATPortMapping(UPNPNAT_MAPPING *mapping, bool tryRandom = false);
  28. UPNPNAT_RETURN RemoveNATPortMapping(UPNPNAT_MAPPING mapping, bool removeFromList = true);
  29. void clearNATPortMapping();
  30. bool RemoveSpecifiedPort(WORD port,UPNPNAT_PROTOCOL protocol);
  31. CString  GetLastError();
  32. CString  GetLocalIPStr();
  33. DWORD  GetLocalIP();
  34. bool  IsLANIP(DWORD nIP);
  35. protected:
  36. void  InitLocalIP();
  37. void  SetLastError(CString error);
  38. bool addPortmap(int eport, int iport, const CString& iclient,
  39.         const CString& descri, const CString& type);
  40. bool deletePortmap(int eport, const CString& type);
  41. bool  isComplete() const { return !m_controlurl.IsEmpty(); }
  42. bool  Search(int version=1);
  43. bool  GetDescription();
  44. CString  GetProperty(const CString& name, CString& response);
  45. bool  InvokeCommand(const CString& name, const CString& args);
  46. bool  Valid()const{return (!m_name.IsEmpty()&&!m_description.IsEmpty());}
  47. bool  InternalSearch(int version);
  48. CString  m_devicename;
  49. CString  m_name;
  50. CString  m_description;
  51. CString  m_baseurl;
  52. CString  m_controlurl;
  53. CString  m_friendlyname;
  54. CString  m_modelname;
  55. int   m_version;
  56. private:
  57. CList<UPNPNAT_MAPPING, UPNPNAT_MAPPING> m_Mappings;
  58. CString  m_slocalIP;
  59. CString  m_slastError;
  60. DWORD  m_uLocalIP;
  61. bool  isSearched;
  62. };
  63. #endif
复制代码
 楼主| 发表于 2007-12-14 00:02:05 | 显示全部楼层
UPNP.cpp
  1. #include "stdafx.h"
  2. #include "upnp.h"
  3. #define UPNPPORTMAP0   _T("WANIPConnection")
  4. #define UPNPPORTMAP1   _T("WANPPPConnection")
  5. #define UPNPGETEXTERNALIP _T("GetExternalIPAddress"),_T("NewExternalIPAddress")
  6. #define UPNPADDPORTMAP _T("AddPortMapping")
  7. #define UPNPDELPORTMAP _T("DeletePortMapping")
  8. static const ulong UPNPADDR = 0xFAFFFFEF;
  9. static const int UPNPPORT = 1900;
  10. static const CString URNPREFIX = _T("urn:schemas-upnp-org:");
  11. const CString getString(int i)
  12. {
  13. CString s;
  14. s.Format(_T("%d"), i);
  15. return s;
  16. }
  17. const CString GetArgString(const CString& name, const CString& value)
  18. {
  19. return _T("<") + name + _T(">") + value + _T("</") + name + _T(">");
  20. }
  21. const CString GetArgString(const CString& name, int value)
  22. {
  23. return _T("<") + name + _T(">") + getString(value) + _T("</") + name + _T(">");
  24. }
  25. bool SOAP_action(CString addr, uint16 port, const CString request, CString &response)
  26. {
  27. char buffer[10240];
  28. const CStringA sa(request);
  29. int length = sa.GetLength();
  30. strcpy(buffer, (const char*)sa);
  31. uint32 ip = inet_addr(CStringA(addr));
  32. struct sockaddr_in sockaddr;
  33. memset(&sockaddr, 0, sizeof(sockaddr));
  34. sockaddr.sin_family = AF_INET;
  35. sockaddr.sin_port = htons(port);
  36. sockaddr.sin_addr.S_un.S_addr = ip;
  37. int s = socket(AF_INET, SOCK_STREAM, 0);
  38. u_long lv = 1;
  39. ioctlsocket(s, FIONBIO, &lv);
  40. connect(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
  41. Sleep(50);
  42. /*int n =*/ send(s, buffer, length, 0);
  43. Sleep(500);
  44. int rlen = recv(s, buffer, sizeof(buffer), 0);
  45. closesocket(s);
  46. if (rlen == SOCKET_ERROR) return false;
  47. if (!rlen) return false;
  48. response = CString(CStringA(buffer, rlen));
  49. return true;
  50. }
  51. int SSDP_sendRequest(int s, uint32 ip, uint16 port, const CString& request)
  52. {
  53. char buffer[10240];
  54. const CStringA sa(request);
  55. int length = sa.GetLength();
  56. strcpy(buffer, (const char*)sa);
  57. struct sockaddr_in sockaddr;
  58. memset(&sockaddr, 0, sizeof(sockaddr));
  59. sockaddr.sin_family = AF_INET;
  60. sockaddr.sin_port = htons(port);
  61. sockaddr.sin_addr.S_un.S_addr = ip;
  62. return sendto(s, buffer, length, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
  63. }
  64. bool parseHTTPResponse(const CString& response, CString& result)
  65. {
  66. int pos = 0;
  67. CString status = response.Tokenize(_T("\r\n"), pos);
  68. result = response;
  69. result.Delete(0, pos);
  70. pos = 0;
  71. status.Tokenize(_T(" "), pos);
  72. status = status.Tokenize(_T(" "), pos);
  73. if (status.IsEmpty() || status[0]!='2') return false;
  74. return true;
  75. }
  76. const CString getProperty(const CString& all, const CString& name)
  77. {
  78. CString startTag = '<' + name + '>';
  79. CString endTag = _T("</") + name + '>';
  80. CString property;
  81. int posStart = all.Find(startTag);
  82. if (posStart<0) return CString();
  83. int posEnd = all.Find(endTag, posStart);
  84. if (posStart>=posEnd) return CString();
  85. return all.Mid(posStart + startTag.GetLength(), posEnd - posStart - startTag.GetLength());
  86. }
  87. MyUPnP::MyUPnP()
  88. : m_version(1)
  89. {
  90. m_uLocalIP = 0;
  91. isSearched = false;
  92. }
  93. MyUPnP::~MyUPnP()
  94. {
  95. UPNPNAT_MAPPING search;
  96. POSITION pos = m_Mappings.GetHeadPosition();
  97. while(pos){
  98.   search = m_Mappings.GetNext(pos);
  99.   RemoveNATPortMapping(search, false);
  100. }
  101. m_Mappings.RemoveAll();
  102. }
  103. bool MyUPnP::InternalSearch(int version)
  104. {
  105. if(version<=0)version = 1;
  106. m_version = version;
  107. #define NUMBEROFDEVICES 2
  108. CString devices[][2] = {
  109.   {UPNPPORTMAP1, _T("service")},
  110.   {UPNPPORTMAP0, _T("service")},
  111.   {_T("InternetGatewayDevice"), _T("device")},
  112. };
  113. int s = socket(AF_INET, SOCK_DGRAM, 0);
  114. u_long lv = 1;
  115. ioctlsocket(s, FIONBIO, &lv);
  116. int rlen = 0;
  117. for (int i=0; rlen<=0 && i<500; i++) {
  118.   if (!(i%100)) {
  119.    for (int i=0; i<NUMBEROFDEVICES; i++) {
  120.     m_name.Format(_T("%s%s:%s:%d"), URNPREFIX, devices[i][1], devices[i][0], version);
  121.     CString request;
  122.     request.Format(_T("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "ssdp:discover"\r\nMX: %d\r\nST: %s\r\n\r\n"),
  123.      6, m_name);
  124.     SSDP_sendRequest(s, UPNPADDR, UPNPPORT, request);
  125.    }
  126.   }
  127.   Sleep(10);
  128.   char buffer[10240];
  129.   rlen = recv(s, buffer, sizeof(buffer), 0);
  130.   if (rlen <= 0) continue;
  131.   closesocket(s);
  132.   CString response = CString(CStringA(buffer, rlen));
  133.   CString result;
  134.   if (!parseHTTPResponse(response, result)) return false;
  135.   for (int d=0; d<NUMBEROFDEVICES; d++) {
  136.    m_name.Format(_T("%s%s:%s:%d"), URNPREFIX, devices[d][1], devices[d][0], version);
  137.    if (result.Find(m_name) >= 0) {
  138.     for (int pos = 0;;) {
  139.      CString line = result.Tokenize(_T("\r\n"), pos);
  140.      if (line.IsEmpty()) return false;
  141.      CString name = line.Mid(0, 9);
  142.      name.MakeUpper();
  143.      if (name == _T("LOCATION:")) {
  144.       line.Delete(0, 9);
  145.       m_description = line;
  146.       m_description.Trim();
  147.       return GetDescription();
  148.      }
  149.     }
  150.    }
  151.   }
  152. }
  153. closesocket(s);
  154. return false;
  155. }
  156. bool MyUPnP::Search(int version)
  157. {
  158. if (isSearched) return isComplete();
  159. isSearched = true;
  160. return InternalSearch(version);
  161. }
  162. static CString NGetAddressFromUrl(const CString& str, CString& post, CString& host, int& port)
  163. {
  164. CString s = str;
  165. post = _T("");
  166. host = post;
  167. port = 0;
  168. int pos = s.Find(_T("://"));
  169. if (!pos) return CString();
  170. s.Delete(0, pos + 3);
  171. pos = s.Find('/');
  172. if (!pos) {
  173.   host = s;
  174.   s = _T("");
  175. } else {
  176.   host = s.Mid(0, pos);
  177.   s.Delete(0, pos);
  178. }
  179. if (s.IsEmpty()) {
  180.   post = _T("");
  181. } else {
  182.   post = s;
  183. }
  184. pos = 0;
  185. CString addr = host.Tokenize(_T(":"), pos);
  186. s = host.Tokenize(_T(":"), pos);
  187. if (s.IsEmpty()) {
  188.   port = 80;
  189. } else {
  190.   port = _tstoi(s);
  191. }
  192. return addr;
  193. }
  194. bool MyUPnP::GetDescription()
  195. {
  196. if(!Valid())return false;
  197. CString post, host, addr;
  198. int port = 0;
  199. addr = NGetAddressFromUrl(m_description, post, host, port);
  200. if(addr.IsEmpty())return false;
  201. CString request = CString(_T("GET ")) + post + _T(" HTTP/1.1\r\nHOST: ") + host + _T("\r\nACCEPT-LANGUAGE: en\r\n\r\n");
  202. CString response;
  203. if (!SOAP_action(addr, (uint16)port, request, response)) return false;
  204. CString result;
  205. if (!parseHTTPResponse(response, result)) return false;
  206. m_friendlyname = getProperty(result, _T("friendlyName"));
  207. m_modelname = getProperty(result, _T("modelName"));
  208. m_baseurl = getProperty(result, _T("URLBase"));
  209. if(m_baseurl.IsEmpty())m_baseurl = CString(_T("http://")) + host + _T("/");
  210. if(m_baseurl[m_baseurl.GetLength() - 1]!='/')m_baseurl += _T("/");
  211. CString serviceType = _T("<serviceType>") + m_name + _T("</serviceType>");
  212. int pos = result.Find(serviceType);
  213. if (pos >= 0) {
  214.   result.Delete(0, pos + serviceType.GetLength());
  215.   pos = result.Find(_T("</service>"));
  216.   if (pos >= 0) {
  217.    result = result.Mid(0, pos);
  218.    m_controlurl = getProperty(result, _T("controlURL"));
  219.    if (!m_controlurl.IsEmpty() && m_controlurl[0] == '/') {
  220.     m_controlurl = m_baseurl + m_controlurl.Mid(1);
  221.    }
  222.   }
  223. }
  224. return isComplete();
  225. }
  226. CString MyUPnP::GetProperty(const CString& name, CString& response)
  227. {
  228. if (!isComplete())return CString();
  229. CString post, host, addr;
  230. int port = 0;
  231. addr = NGetAddressFromUrl(m_controlurl, post, host, port);
  232. if(addr.IsEmpty())return CString();
  233. CString cnt;
  234. CString psr;
  235. cnt.Append(_T("<s:Envelope\r\n    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"\r\n    "));
  236. cnt.Append(_T("s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\r\n  <s:Body>\r\n    <u:"));
  237. cnt.Append(name);
  238. cnt.Append(_T(" xmlns:u=""));
  239. cnt.Append(m_name);
  240. cnt.Append(_T("">\r\n    </u:"));
  241. cnt.Append(name);
  242. cnt.Append(_T(">\r\n  </s:Body>\r\n</s:Envelope>\r\n\r\n"));
  243. psr.Append(_T("POST "));
  244. psr.Append(post);
  245. psr.Append(_T(" HTTP/1.1\r\nHOST: "));
  246. psr.Append(host);
  247. psr.Append(_T("\r\nContent-Length: "));
  248. psr.Append(getString(CStringA(cnt).GetLength()));
  249. psr.Append(_T("\r\nContent-Type: text/xml; charset="utf-8"\r\nSOAPAction: ""));
  250. psr.Append(m_name);
  251. psr.Append(_T("#"));
  252. psr.Append(name);
  253. psr.Append(_T(""\r\n\r\n"));
  254. psr.Append(cnt);
  255. CString request = psr;
  256. if (!SOAP_action(addr, (uint16)port, request, response)) return CString();
  257. CString result;
  258. if (!parseHTTPResponse(response, result)) return CString();
  259. return getProperty(result, response);
  260. }
  261. bool MyUPnP::InvokeCommand(const CString& name, const CString& args)
  262. {
  263. if(!isComplete())return false;
  264. CString post, host, addr;
  265. int port = 0;
  266. addr = NGetAddressFromUrl(m_controlurl, post, host, port);
  267. if(addr.IsEmpty())return false;
  268. CString cnt;
  269. CString psr;
  270. cnt.Append(_T("<?xml version="1.0"?><s:Envelope\r\n    xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"\r\n    "));
  271. cnt.Append(_T("s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\r\n  <s:Body>\r\n    <u:"));
  272. cnt.Append(name);
  273. cnt.Append(_T(" xmlns:u=""));
  274. cnt.Append(m_name);
  275. cnt.Append(_T("">\r\n"));
  276. cnt.Append(args);
  277. cnt.Append(_T("    </u:"));
  278. cnt.Append(name);
  279. cnt.Append(_T(">\r\n  </s:Body>\r\n</s:Envelope>\r\n\r\n"));
  280. psr.Append(_T("POST "));
  281. psr.Append(post);
  282. psr.Append(_T(" HTTP/1.1\r\nHOST: "));
  283. psr.Append(host);
  284. psr.Append(_T("\r\nContent-Length: "));
  285. psr.Append(getString(CStringA(cnt).GetLength()));
  286. psr.Append(_T("\r\nContent-Type: text/xml; charset="utf-8"\r\nSOAPAction: ""));
  287. psr.Append(m_name);
  288. psr.Append(_T("#"));
  289. psr.Append(name);
  290. psr.Append(_T(""\r\n\r\n"));
  291. psr.Append(cnt);
  292. CString response;
  293. CString request = psr;
  294. if (!SOAP_action(addr, (uint16)port, request, response)) return false;
  295. CString result;
  296. if (!parseHTTPResponse(response, result)) return false;
  297. return true;
  298. }
  299. bool MyUPnP::addPortmap(int eport, int iport, const CString& iclient, const CString& descri, const CString& type)
  300. {
  301. CString args;
  302. args.Empty();
  303. args.Append(GetArgString(_T("NewRemoteHost"), _T("")));
  304. args.Append(GetArgString(_T("NewExternalPort"), eport));
  305. args.Append(GetArgString(_T("NewProtocol"), type));
  306. args.Append(GetArgString(_T("NewInternalPort"), iport));
  307. args.Append(GetArgString(_T("NewInternalClient"), iclient));
  308. args.Append(GetArgString(_T("NewEnabled"), _T("1")));
  309. args.Append(GetArgString(_T("NewPortMappingDescription"), descri));
  310. args.Append(GetArgString(_T("NewLeaseDuration"), 0));
  311. return InvokeCommand(UPNPADDPORTMAP, args);
  312. }
  313. bool MyUPnP::deletePortmap(int eport, const CString& type)
  314. {
  315. CString args;
  316. args.Empty();
  317. args.Append(GetArgString(_T("NewRemoteHost"), _T("")));
  318. args.Append(GetArgString(_T("NewExternalPort"), eport));
  319. args.Append(GetArgString(_T("NewProtocol"), type));
  320. return InvokeCommand(UPNPDELPORTMAP, args);
  321. }
  322. /////////////////////////////////////////////////////////////////////////////////
  323. // Adds a NAT Port Mapping
  324. // Params:
  325. //  UPNPNAT_MAPPING *mapping  ->  Port Mapping Data
  326. //   If mapping->externalPort is 0, then
  327. //   mapping->externalPort gets the value of mapping->internalPort
  328. //  bool tryRandom:
  329. //   If If mapping->externalPort is in use, tries to find a free
  330. //   random external port.
  331. //
  332. // Return:
  333. //  UNAT_OK:
  334. //   Successfull.
  335. //  UNAT_EXTERNAL_PORT_IN_USE:
  336. //   Error, you are trying to add a port mapping with an external port
  337. //   in use.
  338. //  UNAT_NOT_IN_LAN:
  339. //   Error, you aren't in a LAN -> no router or firewall
  340. //  UNAT_ERROR:
  341. //   Error, use GetLastError() to get an error description.
  342. /////////////////////////////////////////////////////////////////////////////////
  343. MyUPnP::UPNPNAT_RETURN MyUPnP::AddNATPortMapping(UPNPNAT_MAPPING *mapping, bool tryRandom)
  344. {
  345. CString ProtoStr, Proto;
  346. if(!IsLANIP(GetLocalIP())){
  347.   SetLastError(_T("You aren't behind a Hardware Firewall or Router"));
  348.   return UNAT_NOT_IN_LAN;
  349. }
  350. if (!isComplete()) {
  351.   Search();
  352.   if (!isComplete()) {
  353.    SetLastError(_T("Can not found a UPnP Router"));
  354.    return UNAT_ERROR;
  355.   }
  356. }
  357. if (mapping->protocol == UNAT_TCP){
  358.   Proto = _T("TCP");
  359.   ProtoStr = _T("TCP");
  360. }
  361. else {
  362.   Proto = _T("UDP");
  363.   ProtoStr = _T("UDP");
  364. }
  365. if(mapping->externalPort == 0)
  366.   mapping->externalPort = mapping->internalPort;
  367. //WORD rndPort = mapping->externalPort;
  368. for (int retries = 255; retries; retries--) {
  369.   CString Desc;
  370.   Desc.Format(_T("eMule (%s) [%s: %u]"), mapping->description, ProtoStr, mapping->externalPort);
  371.   if (addPortmap(mapping->externalPort, mapping->internalPort, GetLocalIPStr(), Desc, Proto)) {
  372.    m_Mappings.AddTail(*mapping);
  373.    return UNAT_OK;
  374.   }
  375.   if (!tryRandom) {
  376.    SetLastError(_T("External NAT port in use"));
  377.    return UNAT_EXTERNAL_PORT_IN_USE;
  378.   }
  379.   mapping->externalPort = (WORD)(2049 + (65535 - 2049) * rand() / (RAND_MAX + 1));
  380. }
  381. SetLastError(_T("External NAT port in use: Too many retries"));
  382. return UNAT_EXTERNAL_PORT_IN_USE;
  383. }
  384. /////////////////////////////////////////////////////////////////////////////////
  385. // Removes a NAT Port Mapping
  386. // Params:
  387. //  UPNPNAT_MAPPING *mapping  ->  Port Mapping Data
  388. //   Should be the same struct passed to AddNATPortMapping
  389. //  bool removeFromList -> Remove the port mapping from the internal list
  390. //   Should by allways true (dafault value if not passed).
  391. //   If you set it to false can cause an unexpected error.
  392. //
  393. //
  394. // Return:
  395. //  UNAT_OK:
  396. //   Successfull.
  397. //  UNAT_NOT_OWNED_PORTMAPPING:
  398. //   Error, you are trying to remove a port mapping not owned by this class
  399. //  UNAT_NOT_IN_LAN:
  400. //   Error, you aren't in a LAN -> no router or firewall
  401. //  UNAT_ERROR:
  402. //   Error, use GetLastError() to get an error description.
  403. /////////////////////////////////////////////////////////////////////////////////
  404. MyUPnP::UPNPNAT_RETURN MyUPnP::RemoveNATPortMapping(UPNPNAT_MAPPING mapping, bool removeFromList)
  405. {
  406. if(!IsLANIP(GetLocalIP())){
  407.   SetLastError(_T("You aren't behind a Hardware Firewall or Router"));
  408.   return UNAT_NOT_IN_LAN;
  409. }
  410. if (!isComplete()) {
  411.   Search();
  412.   if (!isComplete()) {
  413.    SetLastError(_T("Can not found a UPnP Router"));
  414.    return UNAT_ERROR;
  415.   }
  416. }
  417. for(POSITION pos = m_Mappings.GetHeadPosition(); pos!=NULL; m_Mappings.GetNext(pos)){
  418.   UPNPNAT_MAPPING search = m_Mappings.GetAt(pos);
  419.   if (search.externalPort == mapping.externalPort
  420.    && search.protocol == mapping.protocol)
  421.   {
  422.    CString Proto;
  423.    if (mapping.protocol == UNAT_TCP)
  424.     Proto = _T("TCP");
  425.    else
  426.     Proto = _T("UDP");
  427.    if (deletePortmap(mapping.externalPort, Proto)) {
  428.     if(removeFromList)
  429.      m_Mappings.RemoveAt(pos);
  430.     return UNAT_OK;
  431.    } else {
  432.     SetLastError(_T("Error getting StaticPortMappingCollection"));
  433.     return UNAT_ERROR;
  434.    }
  435.   }
  436. }
  437. SetLastError(_T("Port mapping not owned by this class"));
  438. return UNAT_NOT_OWNED_PORTMAPPING;
  439. }
  440. void MyUPnP::clearNATPortMapping()
  441. {
  442. UPNPNAT_MAPPING search;
  443. POSITION pos = m_Mappings.GetHeadPosition();
  444. while(pos){
  445.   search = m_Mappings.GetNext(pos);
  446.   RemoveNATPortMapping(search, false);
  447. }
  448. m_Mappings.RemoveAll();
  449. }
  450. bool MyUPnP::RemoveSpecifiedPort(WORD port,UPNPNAT_PROTOCOL protocol)
  451. {
  452. UPNPNAT_MAPPING search;
  453. POSITION pos = m_Mappings.GetHeadPosition();
  454. while(pos){
  455.   POSITION cur_pos = pos;
  456.   search = m_Mappings.GetNext(pos);
  457.   if(search.externalPort==port && search.protocol==protocol)
  458.   {
  459.    RemoveNATPortMapping(search, false);
  460.    m_Mappings.RemoveAt(cur_pos);
  461.    return true;
  462.   }
  463. }
  464. return false;
  465. }
  466. /////////////////////////////////////////////////////////////////////////////////
  467. // Initializes m_localIP variable, for future access to GetLocalIP()
  468. /////////////////////////////////////////////////////////////////////////////////
  469. void MyUPnP::InitLocalIP()
  470. {
  471. #ifndef _DEBUG
  472. try
  473. #endif
  474. {
  475.   char szHost[256];
  476.   if (gethostname(szHost, sizeof szHost) == 0){
  477.    hostent* pHostEnt = gethostbyname(szHost);
  478.    if (pHostEnt != NULL && pHostEnt->h_length == 4 && pHostEnt->h_addr_list[0] != NULL){
  479.     UPNPNAT_MAPPING mapping;
  480.     struct in_addr addr;
  481.     memcpy(&addr, pHostEnt->h_addr_list[0], sizeof(struct in_addr));
  482.     m_slocalIP = inet_ntoa(addr);
  483.     m_uLocalIP = addr.S_un.S_addr;
  484.    }
  485.    else{
  486.     m_slocalIP = _T("");
  487.     m_uLocalIP = 0;
  488.    }
  489.   }
  490.   else{
  491.    m_slocalIP = _T("");
  492.    m_uLocalIP = 0;
  493.   }
  494. }
  495. #ifndef _DEBUG
  496. catch(...){
  497.   m_slocalIP = _T("");
  498.   m_uLocalIP = 0;
  499. }
  500. #endif
  501. }
  502. /////////////////////////////////////////////////////////////////////////////////
  503. // Returns the Local IP
  504. /////////////////////////////////////////////////////////////////////////////////
  505. DWORD MyUPnP::GetLocalIP()
  506. {
  507. if(m_uLocalIP == 0)
  508.   InitLocalIP();
  509. return m_uLocalIP;
  510. }
  511. /////////////////////////////////////////////////////////////////////////////////
  512. // Returns a CString with the local IP in format xxx.xxx.xxx.xxx
  513. /////////////////////////////////////////////////////////////////////////////////
  514. CString MyUPnP::GetLocalIPStr()
  515. {
  516. if(m_slocalIP.IsEmpty())
  517.   InitLocalIP();
  518. return m_slocalIP;
  519. }
  520. /////////////////////////////////////////////////////////////////////////////////
  521. // Sets the value of m_lastError (last error description)
  522. /////////////////////////////////////////////////////////////////////////////////
  523. void MyUPnP::SetLastError(CString error) {
  524. m_slastError = error;
  525. };
  526. /////////////////////////////////////////////////////////////////////////////////
  527. // Returns the last error description in a CString
  528. /////////////////////////////////////////////////////////////////////////////////
  529. CString MyUPnP::GetLastError()
  530. {
  531. return m_slastError;
  532. }
  533. /////////////////////////////////////////////////////////////////////////////////
  534. // Returns true if nIP is a LAN ip, false otherwise
  535. /////////////////////////////////////////////////////////////////////////////////
  536. bool MyUPnP::IsLANIP(DWORD nIP){
  537. // filter LAN IP's
  538. // -------------------------------------------
  539. // 0.*
  540. // 10.0.0.0 - 10.255.255.255  class A
  541. // 172.16.0.0 - 172.31.255.255  class B
  542. // 192.168.0.0 - 192.168.255.255 class C
  543. unsigned char nFirst = (unsigned char)nIP;
  544. unsigned char nSecond = (unsigned char)(nIP >> 8);
  545. if (nFirst==192 && nSecond==168) // check this 1st, because those LANs IPs are mostly spreaded
  546.   return true;
  547. if (nFirst==172 && nSecond>=16 && nSecond<=31)
  548.   return true;
  549. if (nFirst==0 || nFirst==10)
  550.   return true;
  551. return false;
  552. }
复制代码
发表于 2007-12-31 13:38:55 | 显示全部楼层

新手

谢谢版主,正在学习p2p中....感谢acejoy重新开放:)
发表于 2008-4-13 16:01:00 | 显示全部楼层
不错,不错。顶你:lol
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-12-22 16:06 , Processed in 0.018537 second(s), 5 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表