半玩半弄地研究了好几天,终于基本弄清楚了Upnp端口映射的原理,写了一个方便的C++类,虽然代码杂乱。。。
本模块使用了 tinyxml2 库
参考了以下文章
http://www.cnblogs.com/hnrainll/archive/2012/07/27/2612152.html
http://blog.csdn.net/braddoris/article/details/41646789
编写环境 VS2015
c++代码 + 测试,内附tinyxml2库,可以直接编译
360云盘炸了…
已更新…
下载地址 http://cloud.189.cn/t/vIJRfymAVnI3 UpnpPortMapping.7z
使用流程
首先要初始化类
依次调用
Init() 初始化WSA支持库,重复初始化也是没有问题
SearchUpnpRootDevice() 搜索本局域网内的根设备,路由器也是根设备
IsSupportPortMapping() 检查根设备是否支持端口映射功能
如果初始化成功的话,然后就可以随意调用 控制Upnp端口映射的方法了
//增加一个映射
/*
参数说明:
externalPort 路由器的外部端口,路由器暴露在外网的端口号
protocol 协议,只能选 TCP 或 UDP
internalPort 本机端口,路由器暴露在外网的端口映射到本机哪个端口
internalClient 本机IP地址,示例 192.168.0.100
timeout_seconds 多少时间后此映射自动失效,找不到足够的资料,忽略,可能的话以后补上
*/
bool AddPortMapping(unsigned short externalPort, Protocol protocol, unsigned short internalPort, std::string internalClient, std::string portMappingDescription = "Mapping By UpnpPortMapping" /*, unsigned int timeout_seconds*/);
//删除一个映射
/*
externalPort 路由器的外部端口,路由器暴露在外网的端口号
protocol 协议,只能选 TCP 或 UDP
*/
bool DeletePortMapping(unsigned short externalPort, Protocol protocol);
//查询映射信息
bool GetGenericPortMappingEntry(std::vector &_result);
/******************************分界线*****************************************/
main.cpp
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include
#include
#include
#include
#include "UpnpPortMapping.h"
#include
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
using namespace std;
void main()
{
UpnpPortMapping upm;
vector result;
//初始化
bool b = upm.Init();
assert(b);
b = upm.SearchUpnpRootDevice();
assert(b);
b = upm.IsSupportPortMapping();
assert(b);
//调用命令
cout << "增加端口映射前" << endl;
b = upm.GetGenericPortMappingEntry(result);
assert(b);
for (auto i : result)
{
cout << "remoteHost\t\t" << i.remoteHost << endl;
cout << "externalPort\t\t" << i.externalPort << endl;
cout << "protocol\t\t" << i.protocol << endl;
cout << "internalPort\t\t" << i.internalPort << endl;
cout << "internalClient\t\t" << i.internalClient << endl;
cout << "enabled\t\t\t" << i.enabled << endl;
cout << "portMappingDescription\t" << i.portMappingDescription << endl;
cout << "leaseDuration\t\t" << i.leaseDuration << endl << endl;
}
getchar();
b = upm.AddPortMapping(25550, UpnpPortMapping::TCP, 25550, "192.168.0.105");
assert(b);
b = upm.AddPortMapping(25550, UpnpPortMapping::UDP, 25550, "192.168.0.105");
assert(b);
cout << "增加端口映射后" << endl;
b = upm.GetGenericPortMappingEntry(result);
assert(b);
for (auto i : result)
{
cout << "remoteHost\t\t" << i.remoteHost << endl;
cout << "externalPort\t\t" << i.externalPort << endl;
cout << "protocol\t\t" << i.protocol << endl;
cout << "internalPort\t\t" << i.internalPort << endl;
cout << "internalClient\t\t" << i.internalClient << endl;
cout << "enabled\t\t\t" << i.enabled << endl;
cout << "portMappingDescription\t" << i.portMappingDescription << endl;
cout << "leaseDuration\t\t" << i.leaseDuration << endl << endl;
}
getchar();
b = upm.DeletePortMapping(25550, UpnpPortMapping::TCP);
assert(b);
b = upm.DeletePortMapping(25550, UpnpPortMapping::UDP);
assert(b);
b = upm.GetGenericPortMappingEntry(result);
assert(b);
cout << "删除端口映射后" << endl;
for (auto i : result)
{
cout << "remoteHost\t\t" << i.remoteHost << endl;
cout << "externalPort\t\t" << i.externalPort << endl;
cout << "protocol\t\t" << i.protocol << endl;
cout << "internalPort\t\t" << i.internalPort << endl;
cout << "internalClient\t\t" << i.internalClient << endl;
cout << "enabled\t\t\t" << i.enabled << endl;
cout << "portMappingDescription\t" << i.portMappingDescription << endl;
cout << "leaseDuration\t\t" << i.leaseDuration << endl << endl;
}
getchar();
}
UpnpPortMapping.h
#pragma once
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#include
#include
class UpnpPortMapping
{
public:
enum Protocol
{
TCP,
UDP
};
struct PortMappingInformation
{
std::string remoteHost;
std::string externalPort;
std::string protocol;
std::string internalPort;
std::string internalClient;
std::string enabled;
std::string portMappingDescription;
std::string leaseDuration;
};
//初始化
bool Init();
//搜索本网络中的Upnp设备,默认30秒超时
bool SearchUpnpRootDevice(unsigned int timeout_seconds = 30);
//检查目标Upnp设备是否支持端口映射功能
bool IsSupportPortMapping();
//得到最后执行的命令的结果
std::string GetErrorString();
//增加一个映射
/*
参数说明:
externalPort 路由器的外部端口,路由器暴露在外网的端口号
protocol 协议,只能选 TCP 或 UDP
internalPort 本机端口,路由器暴露在外网的端口映射到本机哪个端口
internalClient 本机IP地址,示例 192.168.0.100
timeout_seconds 多少时间后此映射自动失效,找不到足够的资料,忽略,可能的话以后补上
*/
bool AddPortMapping(unsigned short externalPort, Protocol protocol, unsigned short internalPort, std::string internalClient, std::string portMappingDescription = "Mapping By UpnpPortMapping" /*, unsigned int timeout_seconds*/);
//删除一个映射
/*
externalPort 路由器的外部端口,路由器暴露在外网的端口号
protocol 协议,只能选 TCP 或 UDP
*/
bool DeletePortMapping(unsigned short externalPort, Protocol protocol);
//查询映射信息
bool GetGenericPortMappingEntry(std::vector &_result);
private:
std::string result;
std::string igdPath;
std::string routerIp;
std::string routerPort;
std::string controlURL;
std::string serviceType;
};
UpnpPortMapping.cpp
#include "UpnpPortMapping.h"
#include <WinSock2.h>
#include <windows.h>
#include "tinyxml2\tinyxml2.h"
#pragma comment(lib,"ws2_32.lib")
using namespace std;
bool UpnpPortMapping::Init()
{
int err;
WORD versionRequired;
WSADATA wsaData;
versionRequired=MAKEWORD(2, 0);
err = WSAStartup(versionRequired, &wsaData);//协议库的版本信息
if (err)
{
result = "WSA 初始化失败";
return false;
}
result = "NoError";
return true;
}
bool UpnpPortMapping::SearchUpnpRootDevice(unsigned int timeout_seconds)
{
SOCKET su;
su = socket(AF_INET, SOCK_DGRAM, 0);
if (su == INVALID_SOCKET)
{
result = "创建UDP socket 失败";
return false;
}
int nNetTimeout = 2000; //设置超时,两秒
setsockopt(su, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout, sizeof(int));
int err = 0;
string data;
for (size_t i = 0; i < timeout_seconds / 2 + 1; i++)
{
string findRootDevice = string() +
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"MAN:\"ssdp:discover\"\r\n" +
"MX:3\r\n" +
"ST:UPnP:rootdevice\r\n\r\n";
sockaddr_in server ={0};
server.sin_family = AF_INET;
server.sin_port = htons(1900);
server.sin_addr.s_addr = inet_addr("239.255.255.250");
err = sendto(su, findRootDevice.data(), findRootDevice.size(), 0, (sockaddr*)&server, sizeof(server));
if (err == -1)
{
result = "发送搜索根设备请求失败";
return false;
}
data.resize(1024);
int len = sizeof(server);
err = recvfrom(su, (char *)data.data(), data.size(), 0, (sockaddr*)&server, &len);
if (err == -1)
continue;
break;
}
closesocket(su);
if (err == -1)
{
result = "没有发现根设备";
return false;
}
data.resize(err);
if (data.find("200 OK", 0) == string::npos)
{
result = "接收到坏消息,可能是网络不稳定或根设备繁忙";
return false;
}
string loc = "LOCATION: ";
auto pos = data.find(loc, 0);
igdPath = data.substr(pos + loc.size(), data.find("\r\n", pos) - pos - loc.size());
routerIp = string(igdPath.substr(igdPath.find("//") + 2, igdPath.rfind("/") - igdPath.find("//") - 2));
routerPort = routerIp.substr(routerIp.find(':') + 1);
routerIp = routerIp.substr(0, routerIp.find(':'));
result = "NoError";
return true;
}
bool UpnpPortMapping::IsSupportPortMapping()
{
string getXml = string() +
"GET /igd.xml HTTP/1.1\r\n" +
"Host: host:port\r\n\r\n";
getXml.replace(getXml.find("/igd.xml"), string("/igd.xml").size(), igdPath.substr(igdPath.rfind('/')));
getXml.replace(getXml.find("host:port"), string("host:port").size(), routerIp + ':' + routerPort);
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server ={0};
server.sin_family = AF_INET;
server.sin_port = htons(1900);
server.sin_addr.s_addr = inet_addr(routerIp.c_str());
int err = connect(st, (sockaddr *)&server, sizeof(server));
if (err != 0)
{
result = "无法连接Upnp根设备的1900端口";
return false;
}
err = send(st, getXml.c_str(), getXml.size(), 0);
if (err == -1)
{
result = "发送Upnp描述请求信息失败";
closesocket(st);
return false;
}
string data;
data.resize(8192);
err = recv(st, (char *)data.data(), data.size(), MSG_WAITALL); //第四个参数设置为等待所有的数据后才返回
if (err == -1)
{
result = "接收Upnp描述信息失败";
closesocket(st);
return false;
}
data.resize(err);
closesocket(st);
if (data.find("200 OK", 0) == string::npos)
{
result = "接收Upnp描述信息失败";
return false;
}
data.erase(0, data.find(")); //删除http头
tinyxml2::XMLDocument igd;
tinyxml2::XMLPrinter printx;
err = igd.Parse(data.c_str(), data.size());
if (err != tinyxml2::XML_SUCCESS)
{
result = "转换为内存xml失败";
return false;
}
string controlURI, serviceType, URLBase;
tinyxml2::XMLElement *bele = nullptr, *ele = igd.FirstChildElement("root");
if (bele = ele->FirstChildElement("URLBase"))
URLBase = bele->GetText() ? bele->GetText() : "";
if (ele = ele->FirstChildElement("device"))
{
if ((bele = ele->FirstChildElement("deviceType")) &&
(string(bele->GetText()) == "urn:schemas-upnp-org:device:InternetGatewayDevice:1") &&
(ele = ele->FirstChildElement("deviceList")) &&
(ele = ele->FirstChildElement("device")))
{
do
{
if ((bele = ele->FirstChildElement("deviceType")) &&
(string(bele->GetText()) == "urn:schemas-upnp-org:device:WANDevice:1") &&
(ele = ele->FirstChildElement("deviceList")) &&
(ele = ele->FirstChildElement("device")))
{
do
{
if ((bele = ele->FirstChildElement("deviceType")) &&
(string(bele->GetText()) == "urn:schemas-upnp-org:device:WANConnectionDevice:1") &&
(ele = ele->FirstChildElement("serviceList")) &&
(ele = ele->FirstChildElement("service")))
{
do
{
if ((bele = ele->FirstChildElement("serviceType")) &&
((string(serviceType = bele->GetText()) == "urn:schemas-upnp-org:service:WANIPConnection:1") || (string(serviceType = bele->GetText()) == "urn:schemas-upnp-org:service:WANPPPConnection:1")))
{
controlURI = (bele = ele->FirstChildElement("controlURL")) ? bele->GetText() : "";
goto _end;
}
} while (ele = ele->NextSiblingElement("service"));
}
} while (ele = ele->NextSiblingElement("device"));
}
} while (ele = ele->NextSiblingElement("device"));
}
}
_end:
if (controlURI.empty())
{
result = "目标路由不支持UPNP端口映射服务";
return false;
}
this->controlURL = URLBase + controlURI;
this->serviceType = serviceType;
result = "NoError";
return true;
}
bool UpnpPortMapping::AddPortMapping(unsigned short externalPort, Protocol protocol, unsigned short internalPort, string internalClient, string portMappingDescription /*, unsigned int timeout_seconds*/)
{
string Protocal, actionName = "AddPortMapping";
switch (protocol)
{
case UpnpPortMapping::TCP:
Protocal = "TCP";
break;
case UpnpPortMapping::UDP:
Protocal = "UDP";
break;
default:
result = "该协议不存在";
return false;
break;
}
tinyxml2::XMLDocument xml;
//构建xml文档
xml.LinkEndChild(xml.NewDeclaration());
auto *Envelope = xml.NewElement("s:Envelope");
xml.LinkEndChild(Envelope);
Envelope->SetAttribute("xmlns:s", "http://schemas.xmlsoap.org/soap/envelope/");
Envelope->SetAttribute("s:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
auto *Body = xml.NewElement("s:Body");
Envelope->LinkEndChild(Body);
auto *Action = xml.NewElement((string("u:") + actionName).c_str());
Body->LinkEndChild(Action);
Action->SetAttribute("xmlns:u", serviceType.c_str());
auto *ExternalPort1 = xml.NewElement("NewExternalPort");
auto *InternalPort1 = xml.NewElement("NewInternalPort");
auto *Protocal1 = xml.NewElement("NewProtocol");
auto *InternalClient1 = xml.NewElement("NewInternalClient");
auto *LeaseDuration1 = xml.NewElement("NewLeaseDuration");
auto *PortMappingDescription1 = xml.NewElement("NewPortMappingDescription");
ExternalPort1->SetText(to_string(externalPort).c_str());
InternalPort1->SetText(to_string(internalPort).c_str());
Protocal1->SetText(Protocal.c_str());
InternalClient1->SetText(internalClient.c_str());
LeaseDuration1->SetText(""/*LeaseDuration.c_str()*/);
PortMappingDescription1->SetText(portMappingDescription.c_str());
Action->LinkEndChild(ExternalPort1);
Action->LinkEndChild(InternalPort1);
Action->LinkEndChild(Protocal1);
Action->LinkEndChild(InternalClient1);
Action->LinkEndChild(LeaseDuration1);
Action->LinkEndChild(PortMappingDescription1);
auto *NewRemoteHost = xml.NewElement("NewRemoteHost");
auto *NewEnabled = xml.NewElement("NewEnabled");
NewRemoteHost->SetText("");
NewEnabled->SetText(true);
Action->LinkEndChild(NewRemoteHost);
Action->LinkEndChild(NewEnabled);
tinyxml2::XMLPrinter pr;
xml.Print(&pr);
string xml1 = pr.CStr();
string actionDo = string() +
"POST /control/url HTTP/1.1\r\n" +
"HOST: host:port\r\n" +
"CONTENT-TYPE: text/xml;charset=\"utf-8\"\r\n" +
"CONTENT-LENGTH: length\r\n" +
"SOAPACTION:\"serviceType#actionName\"\r\n\r\n";
actionDo.replace(actionDo.find("host:port"), string("host:port").size(), routerIp);
actionDo.replace(actionDo.find("/control/url"), string("/control/url").size(), controlURL);
actionDo.replace(actionDo.find("length"), string("length").size(), to_string(xml1.size()).c_str());
actionDo.replace(actionDo.find("serviceType"), string("serviceType").size(), serviceType.c_str());
actionDo.replace(actionDo.find("actionName"), string("actionName").size(), actionName.c_str());
string postMsg = actionDo + xml1;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server ={0};
server.sin_family = AF_INET;
server.sin_port = htons((unsigned short)stoul(routerPort));
server.sin_addr.s_addr = inet_addr(routerIp.c_str());
int err = connect(st, (sockaddr *)&server, sizeof(server));
if (err != 0)
{
result = "打开路由Upnp端口失败";
closesocket(st);
return false;
}
err = send(st, postMsg.c_str(), postMsg.size(), 0);
postMsg.resize(8192);
err = recv(st, (char *)postMsg.data(), postMsg.size(), MSG_WAITALL); //第四个参数设置为等待所有的数据
postMsg.resize(err);
closesocket(st);
if (postMsg.find("200 OK", 0) == string::npos)
{
result = "创建端口映射失败";
return false;
}
result = "NoError";
return true;
}
bool UpnpPortMapping::DeletePortMapping(unsigned short externalPort, Protocol protocol)
{
string Protocal, actionName = "DeletePortMapping";
switch (protocol)
{
case UpnpPortMapping::TCP:
Protocal = "TCP";
break;
case UpnpPortMapping::UDP:
Protocal = "UDP";
break;
default:
result = "该协议不存在";
return false;
break;
}
tinyxml2::XMLDocument xml;
//构建xml文档
xml.LinkEndChild(xml.NewDeclaration());
auto *Envelope = xml.NewElement("s:Envelope");
xml.LinkEndChild(Envelope);
Envelope->SetAttribute("xmlns:s", "http://schemas.xmlsoap.org/soap/envelope/");
Envelope->SetAttribute("s:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
auto *Body = xml.NewElement("s:Body");
Envelope->LinkEndChild(Body);
auto *Action = xml.NewElement((string("u:") + actionName).c_str());
Body->LinkEndChild(Action);
Action->SetAttribute("xmlns:u", serviceType.c_str());
auto *ExternalPort1 = xml.NewElement("NewExternalPort");
auto *Protocal1 = xml.NewElement("NewProtocol");
ExternalPort1->SetText(to_string(externalPort).c_str());
Protocal1->SetText(Protocal.c_str());
Action->LinkEndChild(ExternalPort1);
Action->LinkEndChild(Protocal1);
auto *NewRemoteHost = xml.NewElement("NewRemoteHost");
NewRemoteHost->SetText("");
Action->LinkEndChild(NewRemoteHost);
tinyxml2::XMLPrinter pr;
xml.Print(&pr);
string xml1 = pr.CStr();
string actionDo = string() +
"POST /control/url HTTP/1.1\r\n" +
"HOST: host:port\r\n" +
"CONTENT-TYPE: text/xml;charset=\"utf-8\"\r\n" +
"CONTENT-LENGTH: length\r\n" +
"SOAPACTION:\"serviceType#actionName\"\r\n\r\n";
actionDo.replace(actionDo.find("host:port"), string("host:port").size(), routerIp);
actionDo.replace(actionDo.find("/control/url"), string("/control/url").size(), controlURL);
actionDo.replace(actionDo.find("length"), string("length").size(), to_string(xml1.size()).c_str());
actionDo.replace(actionDo.find("serviceType"), string("serviceType").size(), serviceType.c_str());
actionDo.replace(actionDo.find("actionName"), string("actionName").size(), actionName.c_str());
string postMsg = actionDo + xml1;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server ={0};
server.sin_family = AF_INET;
server.sin_port = htons((unsigned short)stoul(routerPort));
server.sin_addr.s_addr = inet_addr(routerIp.c_str());
int err = connect(st, (sockaddr *)&server, sizeof(server));
if (err != 0)
{
result = "打开路由Upnp端口失败";
closesocket(st);
return false;
}
err = send(st, postMsg.c_str(), postMsg.size(), 0);
postMsg.resize(8192);
err = recv(st, (char *)postMsg.data(), postMsg.size(), MSG_WAITALL); //第四个参数设置为等待所有的数据
postMsg.resize(err);
closesocket(st);
if (postMsg.find("200 OK", 0) == string::npos)
{
result = "删除端口映射失败";
return false;
}
result = "NoError";
return true;
}
bool UpnpPortMapping::GetGenericPortMappingEntry(vector<PortMappingInformation> &_result)
{
vector<PortMappingInformation> pl;
for (int i = 0;; ++i)
{
string actionName = "GetGenericPortMappingEntry";
tinyxml2::XMLDocument xml;
//构建xml文档
xml.LinkEndChild(xml.NewDeclaration());
auto *Envelope = xml.NewElement("s:Envelope");
xml.LinkEndChild(Envelope);
Envelope->SetAttribute("xmlns:s", "http://schemas.xmlsoap.org/soap/envelope/");
Envelope->SetAttribute("s:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
auto *Body = xml.NewElement("s:Body");
Envelope->LinkEndChild(Body);
auto *Action = xml.NewElement((string("u:") + actionName).c_str());
Body->LinkEndChild(Action);
Action->SetAttribute("xmlns:u", serviceType.c_str());
auto *PortMappingIndex = xml.NewElement("NewPortMappingIndex");
PortMappingIndex->SetText(to_string(i).c_str());
Action->LinkEndChild(PortMappingIndex);
tinyxml2::XMLPrinter pr;
xml.Print(&pr);
string xml1 = pr.CStr();
string actionDo = string() +
"POST /control/url HTTP/1.1\r\n" +
"HOST: host:port\r\n" +
"CONTENT-TYPE: text/xml;charset=\"utf-8\"\r\n" +
"CONTENT-LENGTH: length\r\n" +
"SOAPACTION:\"serviceType#actionName\"\r\n\r\n";
actionDo.replace(actionDo.find("host:port"), string("host:port").size(), routerIp);
actionDo.replace(actionDo.find("/control/url"), string("/control/url").size(), controlURL);
actionDo.replace(actionDo.find("length"), string("length").size(), to_string(xml1.size()).c_str());
actionDo.replace(actionDo.find("serviceType"), string("serviceType").size(), serviceType.c_str());
actionDo.replace(actionDo.find("actionName"), string("actionName").size(), actionName.c_str());
string postMsg = actionDo + xml1;
SOCKET st = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server ={0};
server.sin_family = AF_INET;
server.sin_port = htons((unsigned short)stoul(routerPort));
server.sin_addr.s_addr = inet_addr(routerIp.c_str());
int err = connect(st, (sockaddr *)&server, sizeof(server));
if (err != 0)
{
result = "打开路由Upnp端口失败";
closesocket(st);
return false;
}
err = send(st, postMsg.c_str(), postMsg.size(), 0);
if (err == -1)
{
result = "发送Upnp端口映射查询信息失败";
closesocket(st);
return false;
}
postMsg.resize(8192);
err = recv(st, (char *)postMsg.data(), postMsg.size(), MSG_WAITALL); //第四个参数设置为等待所有的数据
if (err == -1)
{
result = "接收Upnp端口映射查询信息失败";
closesocket(st);
return false;
}
postMsg.resize(err);
closesocket(st);
if (postMsg.find("200 OK") != string::npos)
{
postMsg.erase(0, postMsg.find("));
xml.Clear();
auto err = xml.Parse(postMsg.c_str());
if (err != tinyxml2::XML_SUCCESS)
{
result = "转换为内存xml失败";
return false;
}
tinyxml2::XMLElement *ele = xml.FirstChildElement("SOAP-ENV:Envelope");
if (ele != nullptr)
{
ele = ele->FirstChildElement("SOAP-ENV:Body");
if (ele != nullptr)
{
ele = ele->FirstChildElement("u:GetGenericPortMappingEntryResponse");
if (ele != nullptr)
{
//不同路由情况不同,也许路由不一定把所有的信息都发过来,不一定每个过来的条目都能转换为文本,值有可能为空,此时GetText返回的是0而不是""
auto GetEle = [](char *name, tinyxml2::XMLElement *ele) -> string
{
return ele->FirstChildElement(name) ? (ele->FirstChildElement(name)->GetText() ? ele->FirstChildElement(name)->GetText() : "") : "";
};
PortMappingInformation pmi;
pmi.remoteHost = GetEle("NewRemoteHost", ele);
pmi.externalPort = GetEle("NewExternalPort", ele);
pmi.protocol = GetEle("NewProtocol", ele);
pmi.internalPort = GetEle("NewInternalPort", ele);
pmi.internalClient = GetEle("NewInternalClient", ele);
pmi.enabled = GetEle("NewEnabled", ele);
pmi.portMappingDescription = GetEle("NewPortMappingDescription", ele);
pmi.leaseDuration = GetEle("NewLeaseDuration", ele);
pl.push_back(pmi);
}
}
}
}
else
break;
}
result = "NoError";
_result = pl;
return true;
}