upnp fix with unit test
This commit is contained in:
149
src/upnp.cpp
149
src/upnp.cpp
@@ -689,68 +689,94 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct parse_state
|
char tolower(char c)
|
||||||
{
|
{
|
||||||
parse_state(): found_service(false), exit(false) {}
|
if (c >= 'A' && c <= 'Z') return c + ('a' - 'A');
|
||||||
void reset(char const* st)
|
return c;
|
||||||
{
|
|
||||||
found_service = false;
|
|
||||||
exit = false;
|
|
||||||
service_type = st;
|
|
||||||
}
|
|
||||||
bool found_service;
|
|
||||||
bool exit;
|
|
||||||
std::string top_tag;
|
|
||||||
std::string control_url;
|
|
||||||
char const* service_type;
|
|
||||||
std::string model;
|
|
||||||
};
|
|
||||||
|
|
||||||
void find_control_url(int type, char const* string, parse_state& state)
|
|
||||||
{
|
|
||||||
if (state.exit) return;
|
|
||||||
|
|
||||||
if (type == xml_start_tag)
|
|
||||||
{
|
|
||||||
if ((!state.top_tag.empty() && state.top_tag == "service")
|
|
||||||
|| !strcmp(string, "service"))
|
|
||||||
{
|
|
||||||
state.top_tag = string;
|
|
||||||
}
|
|
||||||
else if (!strcmp(string, "modelName"))
|
|
||||||
{
|
|
||||||
state.top_tag = string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == xml_end_tag)
|
|
||||||
{
|
|
||||||
if (!strcmp(string, "service"))
|
|
||||||
{
|
|
||||||
state.top_tag.clear();
|
|
||||||
if (state.found_service) state.exit = true;
|
|
||||||
}
|
|
||||||
else if (!state.top_tag.empty() && state.top_tag != "service")
|
|
||||||
state.top_tag = "service";
|
|
||||||
}
|
|
||||||
else if (type == xml_string)
|
|
||||||
{
|
|
||||||
if (state.top_tag == "serviceType")
|
|
||||||
{
|
|
||||||
if (!strcmp(string, state.service_type))
|
|
||||||
state.found_service = true;
|
|
||||||
}
|
|
||||||
else if (state.top_tag == "controlURL")
|
|
||||||
{
|
|
||||||
state.control_url = string;
|
|
||||||
if (state.found_service) state.exit = true;
|
|
||||||
}
|
|
||||||
else if (state.top_tag == "modelName")
|
|
||||||
{
|
|
||||||
state.model = string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copy_tolower(std::string& dst, char const* src)
|
||||||
|
{
|
||||||
|
dst.clear();
|
||||||
|
while (*src) dst.push_back(tolower(*src++));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string_equal_nocase(char const* lhs, char const* rhs)
|
||||||
|
{
|
||||||
|
while (tolower(*lhs) == tolower(*rhs))
|
||||||
|
{
|
||||||
|
if (*lhs == 0) return true;
|
||||||
|
++lhs;
|
||||||
|
++rhs;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct parse_state
|
||||||
|
{
|
||||||
|
parse_state(): found_service(false) {}
|
||||||
|
void reset(char const* st)
|
||||||
|
{
|
||||||
|
found_service = false;
|
||||||
|
service_type = st;
|
||||||
|
tag_stack.clear();
|
||||||
|
}
|
||||||
|
bool found_service;
|
||||||
|
std::list<std::string> tag_stack;
|
||||||
|
std::string control_url;
|
||||||
|
char const* service_type;
|
||||||
|
std::string model;
|
||||||
|
std::string url_base;
|
||||||
|
bool top_tags(const char* str1, const char* str2)
|
||||||
|
{
|
||||||
|
std::list<std::string>::reverse_iterator i = tag_stack.rbegin();
|
||||||
|
if (i == tag_stack.rend()) return false;
|
||||||
|
if (!string_equal_nocase(i->c_str(), str2)) return false;
|
||||||
|
++i;
|
||||||
|
if (i == tag_stack.rend()) return false;
|
||||||
|
if (!string_equal_nocase(i->c_str(), str1)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void find_control_url(int type, char const* string, parse_state& state)
|
||||||
|
{
|
||||||
|
if (type == xml_start_tag)
|
||||||
|
{
|
||||||
|
std::string tag;
|
||||||
|
copy_tolower(tag, string);
|
||||||
|
state.tag_stack.push_back(tag);
|
||||||
|
// std::copy(state.tag_stack.begin(), state.tag_stack.end(), std::ostream_iterator<std::string>(std::cout, " "));
|
||||||
|
// std::cout << std::endl;
|
||||||
|
}
|
||||||
|
else if (type == xml_end_tag)
|
||||||
|
{
|
||||||
|
if (!state.tag_stack.empty())
|
||||||
|
state.tag_stack.pop_back();
|
||||||
|
}
|
||||||
|
else if (type == xml_string)
|
||||||
|
{
|
||||||
|
if (state.tag_stack.empty()) return;
|
||||||
|
// std::cout << " " << string << std::endl;
|
||||||
|
if (!state.found_service && state.top_tags("service", "servicetype"))
|
||||||
|
{
|
||||||
|
if (string_equal_nocase(string, state.service_type))
|
||||||
|
state.found_service = true;
|
||||||
|
}
|
||||||
|
else if (state.found_service && state.top_tags("service", "controlurl"))
|
||||||
|
{
|
||||||
|
state.control_url = string;
|
||||||
|
}
|
||||||
|
else if (state.tag_stack.back() == "modelname")
|
||||||
|
{
|
||||||
|
state.model = string;
|
||||||
|
}
|
||||||
|
else if (state.tag_stack.back() == "urlbase")
|
||||||
|
{
|
||||||
|
state.url_base = string;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void upnp::on_upnp_xml(error_code const& e
|
void upnp::on_upnp_xml(error_code const& e
|
||||||
@@ -824,13 +850,14 @@ void upnp::on_upnp_xml(error_code const& e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s.url_base.empty()) d.control_url = s.control_url;
|
||||||
|
else d.control_url = s.url_base + s.control_url;
|
||||||
|
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "found control URL: " << s.control_url << " namespace: "
|
msg << "found control URL: " << s.control_url << " namespace: "
|
||||||
<< d.service_namespace << " in response from " << d.url;
|
<< d.service_namespace << " in response from " << d.url;
|
||||||
log(msg.str());
|
log(msg.str());
|
||||||
|
|
||||||
d.control_url = s.control_url;
|
|
||||||
|
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
std::string auth;
|
std::string auth;
|
||||||
char const* error;
|
char const* error;
|
||||||
|
@@ -123,6 +123,106 @@ void add_and_replace(libtorrent::dht::node_id& dst, libtorrent::dht::node_id con
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char upnp_xml[] =
|
||||||
|
"<root>"
|
||||||
|
"<specVersion>"
|
||||||
|
"<major>1</major>"
|
||||||
|
"<minor>0</minor>"
|
||||||
|
"</specVersion>"
|
||||||
|
"<URLBase>http://192.168.0.1:5678</URLBase>"
|
||||||
|
"<device>"
|
||||||
|
"<deviceType>"
|
||||||
|
"urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
||||||
|
"</deviceType>"
|
||||||
|
"<presentationURL>http://192.168.0.1:80</presentationURL>"
|
||||||
|
"<friendlyName>D-Link Router</friendlyName>"
|
||||||
|
"<manufacturer>D-Link</manufacturer>"
|
||||||
|
"<manufacturerURL>http://www.dlink.com</manufacturerURL>"
|
||||||
|
"<modelDescription>Internet Access Router</modelDescription>"
|
||||||
|
"<modelName>D-Link Router</modelName>"
|
||||||
|
"<UDN>uuid:upnp-InternetGatewayDevice-1_0-12345678900001</UDN>"
|
||||||
|
"<UPC>123456789001</UPC>"
|
||||||
|
"<serviceList>"
|
||||||
|
"<service>"
|
||||||
|
"<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>"
|
||||||
|
"<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>"
|
||||||
|
"<controlURL>/Layer3Forwarding</controlURL>"
|
||||||
|
"<eventSubURL>/Layer3Forwarding</eventSubURL>"
|
||||||
|
"<SCPDURL>/Layer3Forwarding.xml</SCPDURL>"
|
||||||
|
"</service>"
|
||||||
|
"</serviceList>"
|
||||||
|
"<deviceList>"
|
||||||
|
"<device>"
|
||||||
|
"<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>"
|
||||||
|
"<friendlyName>WANDevice</friendlyName>"
|
||||||
|
"<manufacturer>D-Link</manufacturer>"
|
||||||
|
"<manufacturerURL>http://www.dlink.com</manufacturerURL>"
|
||||||
|
"<modelDescription>Internet Access Router</modelDescription>"
|
||||||
|
"<modelName>D-Link Router</modelName>"
|
||||||
|
"<modelNumber>1</modelNumber>"
|
||||||
|
"<modelURL>http://support.dlink.com</modelURL>"
|
||||||
|
"<serialNumber>12345678900001</serialNumber>"
|
||||||
|
"<UDN>uuid:upnp-WANDevice-1_0-12345678900001</UDN>"
|
||||||
|
"<UPC>123456789001</UPC>"
|
||||||
|
"<serviceList>"
|
||||||
|
"<service>"
|
||||||
|
"<serviceType>"
|
||||||
|
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"
|
||||||
|
"</serviceType>"
|
||||||
|
"<serviceId>urn:upnp-org:serviceId:WANCommonInterfaceConfig</serviceId>"
|
||||||
|
"<controlURL>/WANCommonInterfaceConfig</controlURL>"
|
||||||
|
"<eventSubURL>/WANCommonInterfaceConfig</eventSubURL>"
|
||||||
|
"<SCPDURL>/WANCommonInterfaceConfig.xml</SCPDURL>"
|
||||||
|
"</service>"
|
||||||
|
"</serviceList>"
|
||||||
|
"<deviceList>"
|
||||||
|
"<device>"
|
||||||
|
"<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>"
|
||||||
|
"<friendlyName>WAN Connection Device</friendlyName>"
|
||||||
|
"<manufacturer>D-Link</manufacturer>"
|
||||||
|
"<manufacturerURL>http://www.dlink.com</manufacturerURL>"
|
||||||
|
"<modelDescription>Internet Access Router</modelDescription>"
|
||||||
|
"<modelName>D-Link Router</modelName>"
|
||||||
|
"<modelNumber>1</modelNumber>"
|
||||||
|
"<modelURL>http://support.dlink.com</modelURL>"
|
||||||
|
"<serialNumber>12345678900001</serialNumber>"
|
||||||
|
"<UDN>uuid:upnp-WANConnectionDevice-1_0-12345678900001</UDN>"
|
||||||
|
"<UPC>123456789001</UPC>"
|
||||||
|
"<serviceList>"
|
||||||
|
"<service>"
|
||||||
|
"<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>"
|
||||||
|
"<serviceId>urn:upnp-org:serviceId:WANIPConnection</serviceId>"
|
||||||
|
"<controlURL>/WANIPConnection</controlURL>"
|
||||||
|
"<eventSubURL>/WANIPConnection</eventSubURL>"
|
||||||
|
"<SCPDURL>/WANIPConnection.xml</SCPDURL>"
|
||||||
|
"</service>"
|
||||||
|
"</serviceList>"
|
||||||
|
"</device>"
|
||||||
|
"</deviceList>"
|
||||||
|
"</device>"
|
||||||
|
"</deviceList>"
|
||||||
|
"</device>"
|
||||||
|
"</root>";
|
||||||
|
|
||||||
|
struct parse_state
|
||||||
|
{
|
||||||
|
parse_state(): found_service(false) {}
|
||||||
|
void reset(char const* st)
|
||||||
|
{
|
||||||
|
found_service = false;
|
||||||
|
service_type = st;
|
||||||
|
tag_stack.clear();
|
||||||
|
}
|
||||||
|
bool found_service;
|
||||||
|
std::list<std::string> tag_stack;
|
||||||
|
std::string control_url;
|
||||||
|
char const* service_type;
|
||||||
|
std::string model;
|
||||||
|
std::string url_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
void find_control_url(int type, char const* string, parse_state& state);
|
||||||
|
|
||||||
int test_main()
|
int test_main()
|
||||||
{
|
{
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
@@ -323,6 +423,20 @@ int test_main()
|
|||||||
std::cerr << out4 << std::endl;
|
std::cerr << out4 << std::endl;
|
||||||
TEST_CHECK(out4 == "BaPgarbage inside element bracketsSfooFaPgarbage inside element brackets");
|
TEST_CHECK(out4 == "BaPgarbage inside element bracketsSfooFaPgarbage inside element brackets");
|
||||||
|
|
||||||
|
// test upnp xml parser
|
||||||
|
|
||||||
|
parse_state xml_s;
|
||||||
|
xml_s.reset("urn:schemas-upnp-org:service:WANIPConnection:1");
|
||||||
|
xml_parse((char*)upnp_xml, (char*)upnp_xml + sizeof(upnp_xml)
|
||||||
|
, bind(&find_control_url, _1, _2, boost::ref(xml_s)));
|
||||||
|
|
||||||
|
std::cerr << xml_s.url_base << std::endl;
|
||||||
|
std::cerr << xml_s.control_url << std::endl;
|
||||||
|
std::cerr << xml_s.model << std::endl;
|
||||||
|
TEST_CHECK(xml_s.url_base == "http://192.168.0.1:5678");
|
||||||
|
TEST_CHECK(xml_s.control_url == "/WANIPConnection");
|
||||||
|
TEST_CHECK(xml_s.model == "D-Link Router");
|
||||||
|
|
||||||
// test network functions
|
// test network functions
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
|
Reference in New Issue
Block a user