diff --git a/aioupnp/commands.py b/aioupnp/commands.py
index 163ad44..a254216 100644
--- a/aioupnp/commands.py
+++ b/aioupnp/commands.py
@@ -4,6 +4,7 @@ import typing
import logging
from aioupnp.protocols.scpd import scpd_post
from aioupnp.device import Service
+from aioupnp.fault import UPnPError
log = logging.getLogger(__name__)
@@ -35,17 +36,38 @@ class GetGenericPortMappingEntryResponse(typing.NamedTuple):
lease_time: int
-def recast_return(return_annotation, result: typing.Dict[str, typing.Union[int, str]],
+class SCPDRequestDebuggingInfo(typing.NamedTuple):
+ method: str
+ kwargs: typing.Dict[str, typing.Union[str, int, bool]]
+ response_xml: bytes
+ result: typing.Optional[typing.Union[str, int, bool, GetSpecificPortMappingEntryResponse,
+ GetGenericPortMappingEntryResponse]]
+ err: typing.Optional[Exception]
+ ts: float
+
+
+def recast_return(return_annotation, result: typing.Union[str, int, bool, typing.Dict[str, typing.Union[int, str]]],
result_keys: typing.List[str]) -> typing.Optional[
typing.Union[str, int, bool, GetSpecificPortMappingEntryResponse, GetGenericPortMappingEntryResponse]]:
if len(result_keys) == 1:
- single_result = result[result_keys[0]]
+ if isinstance(result, (str, int, bool)):
+ single_result = result
+ else:
+ if result_keys[0] in result:
+ single_result = result[result_keys[0]]
+ else: # check for the field having incorrect capitalization
+ flattened = {k.lower(): v for k, v in result.items()}
+ if result_keys[0].lower() in flattened:
+ single_result = flattened[result_keys[0].lower()]
+ else:
+ raise UPnPError(f"expected response key {result_keys[0]}, got {list(result.keys())}")
if return_annotation is bool:
return soap_bool(single_result)
if return_annotation is str:
return soap_optional_str(single_result)
- return int(result[result_keys[0]]) if result_keys[0] in result else None
+ return None if single_result is None else int(single_result)
elif return_annotation in [GetGenericPortMappingEntryResponse, GetSpecificPortMappingEntryResponse]:
+ assert isinstance(result, dict)
arg_types: typing.Dict[str, typing.Type[typing.Any]] = return_annotation._field_types
assert len(arg_types) == len(result_keys)
recast_results: typing.Dict[str, typing.Optional[typing.Union[str, int, bool]]] = {}
@@ -108,11 +130,7 @@ class SOAPCommands:
self._base_address = base_address
self._port = port
- self._requests: typing.List[typing.Tuple[str, typing.Dict[str, typing.Any], bytes,
- typing.Optional[typing.Union[str, int, bool,
- GetSpecificPortMappingEntryResponse,
- GetGenericPortMappingEntryResponse]],
- typing.Optional[Exception], float]] = []
+ self._request_debug_infos: typing.List[SCPDRequestDebuggingInfo] = []
def is_registered(self, name: str) -> bool:
if name not in self.SOAP_COMMANDS:
@@ -147,11 +165,17 @@ class SOAPCommands:
)
if err is not None:
assert isinstance(xml_bytes, bytes)
- self._requests.append((name, kwargs, xml_bytes, None, err, time.time()))
+ self._request_debug_infos.append(SCPDRequestDebuggingInfo(name, kwargs, xml_bytes, None, err, time.time()))
raise err
assert 'return' in annotations
- result = recast_return(annotations['return'], response, output_names)
- self._requests.append((name, kwargs, xml_bytes, result, None, time.time()))
+ try:
+ result = recast_return(annotations['return'], response, output_names)
+ self._request_debug_infos.append(SCPDRequestDebuggingInfo(name, kwargs, xml_bytes, result, None, time.time()))
+ except Exception as err:
+ if isinstance(err, asyncio.CancelledError):
+ raise
+ self._request_debug_infos.append(SCPDRequestDebuggingInfo(name, kwargs, xml_bytes, None, err, time.time()))
+ raise UPnPError(f"Raised {str(type(err).__name__)}({str(err)}) parsing response for {name}")
return result
if not len(list(k for k in annotations if k != 'return')):
diff --git a/aioupnp/gateway.py b/aioupnp/gateway.py
index cd2a68c..95dd81c 100644
--- a/aioupnp/gateway.py
+++ b/aioupnp/gateway.py
@@ -6,7 +6,7 @@ from collections import OrderedDict
from typing import Dict, List
from aioupnp.util import get_dict_val_case_insensitive
from aioupnp.constants import SPEC_VERSION, SERVICE
-from aioupnp.commands import SOAPCommands
+from aioupnp.commands import SOAPCommands, SCPDRequestDebuggingInfo
from aioupnp.device import Device, Service
from aioupnp.protocols.ssdp import fuzzy_m_search, m_search
from aioupnp.protocols.scpd import scpd_get
@@ -85,7 +85,7 @@ class Gateway:
self.urn: bytes = (ok_packet.st or '').encode()
self._xml_response: bytes = b""
- self._service_descriptors: Dict[str, bytes] = {}
+ self._service_descriptors: Dict[str, str] = {}
self.base_address, self.port = parse_location(self.location)
self.base_ip = self.base_address.lstrip(b"http://").split(b":")[0]
@@ -103,17 +103,6 @@ class Gateway:
self._registered_commands: Dict[str, str] = {}
self.commands = SOAPCommands(self._loop, self.base_ip, self.port)
- # def gateway_descriptor(self) -> dict:
- # r = {
- # 'server': self.server.decode(),
- # 'urlBase': self.url_base,
- # 'location': self.location.decode(),
- # "specVersion": self.spec_version,
- # 'usn': self.usn.decode(),
- # 'urn': self.urn.decode(),
- # }
- # return r
-
@property
def manufacturer_string(self) -> str:
manufacturer_string = "UNKNOWN GATEWAY"
@@ -147,37 +136,26 @@ class Gateway:
# return service
# return None
- # @property
- # def soap_requests(self) -> typing.List[typing.Tuple[str, typing.Dict[str, typing.Any], bytes,
- # typing.Optional[typing.Tuple],
- # typing.Optional[Exception], float]]:
- # soap_call_infos: typing.List[typing.Tuple[str, typing.Dict[str, typing.Any], bytes,
- # typing.Optional[typing.Tuple],
- # typing.Optional[Exception], float]] = []
- # soap_call_infos.extend([
- # (name, request_args, raw_response, decoded_response, soap_error, ts)
- # for (
- # name, request_args, raw_response, decoded_response, soap_error, ts
- # ) in self.commands._requests
- # ])
- # soap_call_infos.sort(key=lambda x: x[5])
- # return soap_call_infos
-
- # def debug_gateway(self) -> Dict[str, Union[str, bytes, int, Dict, List]]:
- # return {
- # 'manufacturer_string': self.manufacturer_string,
- # 'gateway_address': self.base_ip,
- # 'gateway_descriptor': self.gateway_descriptor(),
- # 'gateway_xml': self._xml_response,
- # 'services_xml': self._service_descriptors,
- # 'services': {service.SCPDURL: service.as_dict() for service in self._services},
- # 'm_search_args': [(k, v) for (k, v) in self._m_search_args.items()],
- # 'reply': self._ok_packet.as_dict(),
- # 'soap_port': self.port,
- # 'registered_soap_commands': self._registered_commands,
- # 'unsupported_soap_commands': self._unsupported_actions,
- # 'soap_requests': self.soap_requests
- # }
+ def debug_gateway(self) -> Dict[str, typing.Union[str, bytes, int, Dict, List]]:
+ return {
+ 'manufacturer_string': self.manufacturer_string,
+ 'gateway_address': self.base_ip.decode(),
+ 'server': self.server.decode(),
+ 'urlBase': self.url_base or '',
+ 'location': self.location.decode(),
+ "specVersion": self.spec_version or '',
+ 'usn': self.usn.decode(),
+ 'urn': self.urn.decode(),
+ 'gateway_xml': self._xml_response.decode(),
+ 'services_xml': self._service_descriptors,
+ 'services': {service.SCPDURL: service.as_dict() for service in self._services},
+ 'm_search_args': OrderedDict(self._m_search_args),
+ 'reply': self._ok_packet.as_dict(),
+ 'soap_port': self.port,
+ 'registered_soap_commands': self._registered_commands,
+ 'unsupported_soap_commands': self._unsupported_actions,
+ 'soap_requests': list(self.commands._request_debug_infos)
+ }
@classmethod
async def _discover_gateway(cls, lan_address: str, gateway_address: str, timeout: int = 30,
@@ -201,7 +179,7 @@ class Gateway:
try:
gateway = cls(datagram, m_search_args, lan_address, gateway_address, loop=loop)
log.debug('get gateway descriptor %s', datagram.location)
- await gateway.discover_commands(loop)
+ await gateway.discover_commands()
requirements_met = all([gateway.commands.is_registered(required) for required in required_commands])
if not requirements_met:
not_met = [
@@ -249,8 +227,10 @@ class Gateway:
results: typing.List['asyncio.Future[Gateway]'] = list(done)
return results[0].result()
- async def discover_commands(self, loop: typing.Optional[asyncio.AbstractEventLoop] = None) -> None:
- response, xml_bytes, get_err = await scpd_get(self.path.decode(), self.base_ip.decode(), self.port, loop=loop)
+ async def discover_commands(self) -> None:
+ response, xml_bytes, get_err = await scpd_get(
+ self.path.decode(), self.base_ip.decode(), self.port, loop=self._loop
+ )
self._xml_response = xml_bytes
if get_err is not None:
raise get_err
@@ -286,7 +266,7 @@ class Gateway:
else:
self._device = Device(self._devices, self._services)
for service_type in self.services.keys():
- await self.register_commands(self.services[service_type], loop)
+ await self.register_commands(self.services[service_type], self._loop)
return None
async def register_commands(self, service: Service,
@@ -298,7 +278,7 @@ class Gateway:
log.debug("get descriptor for %s from %s", service.serviceType, service.SCPDURL)
service_dict, xml_bytes, get_err = await scpd_get(service.SCPDURL, self.base_ip.decode(), self.port, loop=loop)
- self._service_descriptors[service.SCPDURL] = xml_bytes
+ self._service_descriptors[service.SCPDURL] = xml_bytes.decode()
if get_err is not None:
log.debug("failed to get descriptor for %s from %s", service.serviceType, service.SCPDURL)
diff --git a/aioupnp/protocols/scpd.py b/aioupnp/protocols/scpd.py
index 9412946..a85baab 100644
--- a/aioupnp/protocols/scpd.py
+++ b/aioupnp/protocols/scpd.py
@@ -45,7 +45,7 @@ class SCPDHTTPClientProtocol(Protocol):
and devices respond with an invalid HTTP version line
"""
- def __init__(self, message: bytes, finished: 'asyncio.Future[typing.Tuple[bytes, int, bytes]]',
+ def __init__(self, message: bytes, finished: 'asyncio.Future[typing.Tuple[bytes, bytes, int, bytes]]',
soap_method: typing.Optional[str] = None, soap_service_id: typing.Optional[str] = None) -> None:
self.message = message
self.response_buff = b""
@@ -85,7 +85,7 @@ class SCPDHTTPClientProtocol(Protocol):
self._got_headers = True
body = b'\r\n'.join(self.response_buff.split(b'\r\n')[i+1:])
if self._content_length == len(body):
- self.finished.set_result((body, self._response_code, self._response_msg))
+ self.finished.set_result((self.response_buff, body, self._response_code, self._response_msg))
elif self._content_length > len(body):
pass
else:
@@ -105,7 +105,7 @@ async def scpd_get(control_url: str, address: str, port: int,
typing.Dict[str, typing.Any], bytes, typing.Optional[Exception]]:
loop = loop or asyncio.get_event_loop()
packet = serialize_scpd_get(control_url, address)
- finished: 'asyncio.Future[typing.Tuple[bytes, int, bytes]]' = asyncio.Future(loop=loop)
+ finished: 'asyncio.Future[typing.Tuple[bytes, bytes, int, bytes]]' = asyncio.Future(loop=loop)
proto_factory: typing.Callable[[], SCPDHTTPClientProtocol] = lambda: SCPDHTTPClientProtocol(packet, finished)
connect_tup: typing.Tuple[asyncio.BaseTransport, asyncio.BaseProtocol] = await loop.create_connection(
proto_factory, address, port
@@ -115,24 +115,25 @@ async def scpd_get(control_url: str, address: str, port: int,
assert isinstance(protocol, SCPDHTTPClientProtocol)
error = None
- wait_task: typing.Awaitable[typing.Tuple[bytes, int, bytes]] = asyncio.wait_for(protocol.finished, 1.0, loop=loop)
+ wait_task: typing.Awaitable[typing.Tuple[bytes, bytes, int, bytes]] = asyncio.wait_for(protocol.finished, 1.0, loop=loop)
+ body = b''
+ raw_response = b''
try:
- body, response_code, response_msg = await wait_task
+ raw_response, body, response_code, response_msg = await wait_task
except asyncio.TimeoutError:
error = UPnPError("get request timed out")
- body = b''
except UPnPError as err:
error = err
- body = protocol.response_buff
+ raw_response = protocol.response_buff
finally:
transport.close()
if not error:
try:
- return deserialize_scpd_get_response(body), body, None
+ return deserialize_scpd_get_response(body), raw_response, None
except Exception as err:
error = UPnPError(err)
- return {}, body, error
+ return {}, raw_response, error
async def scpd_post(control_url: str, address: str, port: int, method: str, param_names: list, service_id: bytes,
@@ -140,7 +141,7 @@ async def scpd_post(control_url: str, address: str, port: int, method: str, para
**kwargs: typing.Dict[str, typing.Any]
) -> typing.Tuple[typing.Dict, bytes, typing.Optional[Exception]]:
loop = loop or asyncio.get_event_loop()
- finished: 'asyncio.Future[typing.Tuple[bytes, int, bytes]]' = asyncio.Future(loop=loop)
+ finished: 'asyncio.Future[typing.Tuple[bytes, bytes, int, bytes]]' = asyncio.Future(loop=loop)
packet = serialize_soap_post(method, param_names, service_id, address.encode(), control_url.encode(), **kwargs)
proto_factory: typing.Callable[[], SCPDHTTPClientProtocol] = lambda:\
SCPDHTTPClientProtocol(packet, finished, soap_method=method, soap_service_id=service_id.decode())
@@ -152,18 +153,17 @@ async def scpd_post(control_url: str, address: str, port: int, method: str, para
assert isinstance(protocol, SCPDHTTPClientProtocol)
try:
- wait_task: typing.Awaitable[typing.Tuple[bytes, int, bytes]] = asyncio.wait_for(finished, 1.0, loop=loop)
- body, response_code, response_msg = await wait_task
+ wait_task: typing.Awaitable[typing.Tuple[bytes, bytes, int, bytes]] = asyncio.wait_for(finished, 1.0, loop=loop)
+ raw_response, body, response_code, response_msg = await wait_task
except asyncio.TimeoutError:
return {}, b'', UPnPError("Timeout")
except UPnPError as err:
return {}, protocol.response_buff, err
finally:
- # raw_response = protocol.response_buff
transport.close()
try:
return (
- deserialize_soap_post_response(body, method, service_id.decode()), body, None
+ deserialize_soap_post_response(body, method, service_id.decode()), raw_response, None
)
except Exception as err:
- return {}, body, UPnPError(err)
+ return {}, raw_response, UPnPError(err)
diff --git a/aioupnp/serialization/soap.py b/aioupnp/serialization/soap.py
index 9ebc07a..9ee2c08 100644
--- a/aioupnp/serialization/soap.py
+++ b/aioupnp/serialization/soap.py
@@ -1,5 +1,6 @@
import re
import typing
+import json
from aioupnp.util import flatten_keys
from aioupnp.fault import UPnPError
from aioupnp.constants import XML_VERSION, ENVELOPE, BODY, FAULT, CONTROL
@@ -54,7 +55,10 @@ def deserialize_soap_post_response(response: bytes, method: str,
fault: typing.Dict[str, typing.Dict[str, typing.Dict[str, str]]] = flatten_keys(
response_body[FAULT], "{%s}" % CONTROL
)
- raise UPnPError(fault['detail']['UPnPError']['errorDescription'])
+ try:
+ raise UPnPError(fault['detail']['UPnPError']['errorDescription'])
+ except (KeyError, TypeError, ValueError):
+ raise UPnPError(f"Failed to decode error response: {json.dumps(fault)}")
response_key = None
for key in response_body:
if method in key:
diff --git a/tests/protocols/test_scpd.py b/tests/protocols/test_scpd.py
index 6ff5442..3d991f9 100644
--- a/tests/protocols/test_scpd.py
+++ b/tests/protocols/test_scpd.py
@@ -126,7 +126,7 @@ class TestSCPDGet(AsyncioTestCase):
with mock_tcp_and_udp(self.loop, tcp_replies=replies, sent_tcp_packets=sent):
result, raw, err = await scpd_get(self.path, self.lan_address, self.port, self.loop)
self.assertDictEqual({}, result)
- self.assertEqual(self.bad_xml, raw)
+ self.assertEqual(self.bad_response, raw)
self.assertTrue(isinstance(err, UPnPError))
self.assertTrue(str(err).startswith('no element found'))
@@ -187,7 +187,7 @@ class TestSCPDPost(AsyncioTestCase):
self.path, self.gateway_address, self.port, self.method, self.param_names, self.st, self.loop
)
self.assertEqual(None, err)
- self.assertEqual(self.envelope, raw)
+ self.assertEqual(self.post_response, raw)
self.assertDictEqual({'NewExternalIPAddress': '11.22.33.44'}, result)
async def test_scpd_post_timeout(self):
@@ -211,7 +211,7 @@ class TestSCPDPost(AsyncioTestCase):
)
self.assertTrue(isinstance(err, UPnPError))
self.assertTrue(str(err).startswith('no element found'))
- self.assertEqual(self.bad_envelope, raw)
+ self.assertEqual(self.bad_envelope_response, raw)
self.assertDictEqual({}, result)
async def test_scpd_post_overrun_response(self):
diff --git a/tests/serialization/test_soap.py b/tests/serialization/test_soap.py
index ea3cf04..0105498 100644
--- a/tests/serialization/test_soap.py
+++ b/tests/serialization/test_soap.py
@@ -58,6 +58,16 @@ class TestSOAPSerialization(unittest.TestCase):
b"\r\n" \
b"\n\n\t\n\t\t\n\t\t\ts:Client\n\t\t\tUPnPError\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t713\n\t\t\t\t\tSpecifiedArrayIndexInvalid\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n"
+ error_response_no_description = b"HTTP/1.1 500 Internal Server Error\r\n" \
+ b"Server: WebServer\r\n" \
+ b"Date: Thu, 11 Oct 2018 22:16:17 GMT\r\n" \
+ b"Connection: close\r\n" \
+ b"CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n" \
+ b"CONTENT-LENGTH: 429 \r\n" \
+ b"EXT:\r\n" \
+ b"\r\n" \
+ b"\n\n\t\n\t\t\n\t\t\ts:Client\n\t\t\tUPnPError\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t713\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\n"
+
def test_serialize_post(self):
self.assertEqual(serialize_soap_post(
self.method, self.param_names, self.st, self.gateway_address, self.path, **self.kwargs
@@ -94,3 +104,13 @@ class TestSOAPSerialization(unittest.TestCase):
raised = True
self.assertTrue(str(err) == 'SpecifiedArrayIndexInvalid')
self.assertTrue(raised)
+
+ def test_raise_from_error_response_without_error_description(self):
+ raised = False
+ expected = 'Failed to decode error response: {"faultcode": "s:Client", "faultstring": "UPnPError", "detail": {"UPnPError": {"errorCode": "713"}}}'
+ try:
+ deserialize_soap_post_response(self.error_response_no_description, self.method, service_id=self.st.decode())
+ except UPnPError as err:
+ raised = True
+ self.assertTrue(str(err) == expected)
+ self.assertTrue(raised)
diff --git a/tests/test_gateway.py b/tests/test_gateway.py
index 1680ed4..1512947 100644
--- a/tests/test_gateway.py
+++ b/tests/test_gateway.py
@@ -127,133 +127,175 @@ class TestParseActionList(AsyncioTestCase):
('RequestConnection', [], []), ('ForceTermination', [], []),
('GetStatusInfo', [], ['NewConnectionStatus', 'NewLastConnectionError', 'NewUptime']),
('GetNATRSIPStatus', [], ['NewRSIPAvailable', 'NewNATEnabled']), (
- 'GetGenericPortMappingEntry', ['NewPortMappingIndex'],
- ['NewRemoteHost', 'NewExternalPort', 'NewProtocol', 'NewInternalPort', 'NewInternalClient',
- 'NewEnabled', 'NewPortMappingDescription', 'NewLeaseDuration']), (
- 'GetSpecificPortMappingEntry', ['NewRemoteHost', 'NewExternalPort', 'NewProtocol'],
- ['NewInternalPort', 'NewInternalClient', 'NewEnabled', 'NewPortMappingDescription',
- 'NewLeaseDuration']), ('AddPortMapping',
- ['NewRemoteHost', 'NewExternalPort', 'NewProtocol', 'NewInternalPort',
- 'NewInternalClient', 'NewEnabled', 'NewPortMappingDescription',
- 'NewLeaseDuration'], []),
+ 'GetGenericPortMappingEntry', ['NewPortMappingIndex'],
+ ['NewRemoteHost', 'NewExternalPort', 'NewProtocol', 'NewInternalPort', 'NewInternalClient',
+ 'NewEnabled', 'NewPortMappingDescription', 'NewLeaseDuration']), (
+ 'GetSpecificPortMappingEntry', ['NewRemoteHost', 'NewExternalPort', 'NewProtocol'],
+ ['NewInternalPort', 'NewInternalClient', 'NewEnabled', 'NewPortMappingDescription',
+ 'NewLeaseDuration']), ('AddPortMapping',
+ ['NewRemoteHost', 'NewExternalPort', 'NewProtocol', 'NewInternalPort',
+ 'NewInternalClient', 'NewEnabled', 'NewPortMappingDescription',
+ 'NewLeaseDuration'], []),
('DeletePortMapping', ['NewRemoteHost', 'NewExternalPort', 'NewProtocol'], []),
('GetExternalIPAddress', [], ['NewExternalIPAddress'])]
self.assertEqual(expected, get_action_list(self.test_action_list))
class TestDiscoverDLinkDIR890L(AsyncioTestCase):
- gateway_address = "10.0.0.1"
+ gateway_info = \
+ {'manufacturer_string': 'D-Link DIR-890L', 'gateway_address': '10.0.0.1',
+ 'server': 'Linux, UPnP/1.0, DIR-890L Ver 1.20', 'urlBase': 'http://10.0.0.1:49152',
+ 'location': 'http://10.0.0.1:49152/InternetGatewayDevice.xml', 'specVersion': {'major': '1', 'minor': '0'},
+ 'usn': 'uuid:11111111-2222-3333-4444-555555555555::urn:schemas-upnp-org:device:WANDevice:1',
+ 'urn': 'urn:schemas-upnp-org:device:WANDevice:1',
+ 'gateway_xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 3921\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\thttp://10.0.0.1:49152\n\t\n\t\turn:schemas-upnp-org:device:InternetGatewayDevice:1\n\t\tWireless Broadband Router\n\t\tD-Link Corporation\n\t\thttp://www.dlink.com\n\t\tD-Link Router\n\t\tD-Link Router\n\t\tDIR-890L\n\t\thttp://www.dlink.com\n\t\t120\n\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\n\t\t\t\n\t\t\t\timage/gif\n\t\t\t\t118\n\t\t\t\t119\n\t\t\t\t8\n\t\t\t\t/ligd.gif\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\turn:schemas-microsoft-com:service:OSInfo:1\n\t\t\t\turn:microsoft-com:serviceId:OSInfo1\n\t\t\t\t/soap.cgi?service=OSInfo1\n\t\t\t\t/gena.cgi?service=OSInfo1\n\t\t\t\t/OSInfo.xml\n\t\t\t\n\t\t\t\n\t\t\t\turn:schemas-upnp-org:service:Layer3Forwarding:1\n\t\t\t\turn:upnp-org:serviceId:L3Forwarding1\n\t\t\t\t/soap.cgi?service=L3Forwarding1\n\t\t\t\t/gena.cgi?service=L3Forwarding1\n\t\t\t\t/Layer3Forwarding.xml\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\turn:schemas-upnp-org:device:WANDevice:1\n\t\t\t\tWANDevice\n\t\t\t\tD-Link\n\t\t\t\thttp://www.dlink.com\n\t\t\t\tWANDevice\n\t\t\t\tDIR-890L\n\t\t\t\t1\n\t\t\t\thttp://www.dlink.com\n\t\t\t\t120\n\t\t\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\turn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\n\t\t\t\t\t\turn:upnp-org:serviceId:WANCommonIFC1\n\t\t\t\t\t\t/soap.cgi?service=WANCommonIFC1\n\t\t\t\t\t\t/gena.cgi?service=WANCommonIFC1\n\t\t\t\t\t\t/WANCommonInterfaceConfig.xml\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\turn:schemas-upnp-org:device:WANConnectionDevice:1\n\t\t\t\t\t\tWANConnectionDevice\n\t\t\t\t\t\tD-Link\n\t\t\t\t\t\thttp://www.dlink.com\n\t\t\t\t\t\tWanConnectionDevice\n\t\t\t\t\t\tDIR-890L\n\t\t\t\t\t\t1\n\t\t\t\t\t\thttp://www.dlink.com\n\t\t\t\t\t\t120\n\t\t\t\t\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\turn:schemas-upnp-org:service:WANEthernetLinkConfig:1\n\t\t\t\t\t\t\t\turn:upnp-org:serviceId:WANEthLinkC1\n\t\t\t\t\t\t\t\t/soap.cgi?service=WANEthLinkC1\n\t\t\t\t\t\t\t\t/gena.cgi?service=WANEthLinkC1\n\t\t\t\t\t\t\t\t/WANEthernetLinkConfig.xml\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\turn:schemas-upnp-org:service:WANIPConnection:1\n\t\t\t\t\t\t\t\turn:upnp-org:serviceId:WANIPConn1\n\t\t\t\t\t\t\t\t/soap.cgi?service=WANIPConn1\n\t\t\t\t\t\t\t\t/gena.cgi?service=WANIPConn1\n\t\t\t\t\t\t\t\t/WANIPConnection.xml\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\thttp://10.0.0.1\n\t\n\n',
+ 'services_xml': {
+ '/OSInfo.xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 219\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\n\t\n\t\n\n',
+ '/Layer3Forwarding.xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 920\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\t\n\t\t\tGetDefaultConnectionService\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewDefaultConnectionService\n\t\t\t\t\tout\n\t\t\t\t\tDefaultConnectionService\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\tSetDefaultConnectionService\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewDefaultConnectionService\n\t\t\t\t\tin\n\t\t\t\t\tDefaultConnectionService\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\tDefaultConnectionService\n\t\t\tstring\n\t\t\n\t\n\n',
+ '/WANCommonInterfaceConfig.xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 5343\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\r\n\r\n\t\r\n\t\t1\r\n\t\t0\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tGetCommonLinkProperties\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewWANAccessType\r\n\t\t\t\t\tout\r\n\t\t\t\t\tWANAccessType\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLayer1UpstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1UpstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLayer1DownstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPhysicalLinkStatus\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPhysicalLinkStatus\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalBytesSent\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalBytesSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalBytesReceived\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalBytesReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalPacketsSent\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalPacketsSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalPacketsReceived\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalPacketsReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tX_GetICSStatistics\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tUptime\r\n\t\t\t\t\tout\r\n\t\t\t\t\tX_Uptime\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tWANAccessType\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tDSL\r\n\t\t\t\tPOTS\r\n\t\t\t\tCable\r\n\t\t\t\tEthernet\r\n\t\t\t\tOther\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tLayer1UpstreamMaxBitRate\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tPhysicalLinkStatus\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tUp\r\n\t\t\t\tDown\r\n\t\t\t\tInitializing\r\n\t\t\t\tUnavailable\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tWANAccessProvider\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tMaximumActiveConnections\r\n\t\t\tui2\r\n\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tTotalBytesSent\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalBytesReceived\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalPacketsSent\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalPacketsReceived\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tX_PersonalFirewallEnabled\r\n\t\t\tboolean\r\n\t\t\r\n\t\t\r\n\t\t\tX_Uptime\r\n\t\t\tui4\r\n\t\t\r\n\t\r\n\r\n',
+ '/WANEthernetLinkConfig.xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 773\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\t\n\t\t\tGetEthernetLinkStatus\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewEthernetLinkStatus\n\t\t\t\t\tout\n\t\t\t\t\tEthernetLinkStatus\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\tEthernetLinkStatus\n\t\t\tstring\n\t\t\t\n\t\t\t\tUp\n\t\t\t\tDown\n\t\t\t\tUnavailable\n\t\t\t\n\t\t\n\t\n\n',
+ '/WANIPConnection.xml': 'HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 12078\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\r\n\r\n\t\r\n\t\t1\r\n\t\t0\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tSetConnectionType\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionType\r\n\t\t\t\t\tin\r\n\t\t\t\t\tConnectionType\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t \r\n\t\t\r\n\t\t\tGetConnectionTypeInfo\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionType\r\n\t\t\t\t\tout\r\n\t\t\t\t\tConnectionType\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPossibleConnectionTypes\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPossibleConnectionTypes\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tRequestConnection\r\n\t\t\r\n\t\t\r\n\t\t\tForceTermination\r\n\t\t\r\n\t\t\r\n\t\t\tGetStatusInfo\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionStatus\r\n\t\t\t\t\tout\r\n\t\t\t\t\tConnectionStatus\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLastConnectionError\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLastConnectionError\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewUptime\r\n\t\t\t\t\tout\r\n\t\t\t\t\tUptime\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetNATRSIPStatus\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRSIPAvailable\r\n\t\t\t\t\tout\r\n\t\t\t\t\tRSIPAvailable\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewNATEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tNATEnabled\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetGenericPortMappingEntry\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingIndex\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingNumberOfEntries\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tout\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetSpecificPortMappingEntry\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tAddPortMapping\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tin\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tDeletePortMapping\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetExternalIPAddress\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalIPAddress\r\n\t\t\t\t\tout\r\n\t\t\t\t\tExternalIPAddress\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tConnectionType\r\n\t\t\tstring\r\n\t\t\tUnconfigured\r\n\t\t\r\n\t\t\r\n\t\t\tPossibleConnectionTypes\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tUnconfigured\r\n\t\t\t\tIP_Routed\r\n\t\t\t\tIP_Bridged\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tConnectionStatus\r\n\t\t\tstring\r\n\t\t\tUnconfigured\r\n\t\t\t\r\n\t\t\t\tUnconfigured\r\n\t\t\t\tConnecting\r\n\t\t\t\tAuthenticating\r\n\t\t\t\tPendingDisconnect\r\n\t\t\t\tDisconnecting\r\n\t\t\t\tDisconnected\r\n\t\t\t\tConnected\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tUptime\r\n\t\t\tui4\r\n\t\t\t0\r\n\t\t\t\r\n\t\t\t\t0\r\n\t\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tRSIPAvailable\r\n\t\tboolean\r\n\t\t\t0\r\n\t\t\r\n\t\t\r\n\t\t\tNATEnabled\r\n\t\t\tboolean\r\n\t\t\t1\r\n\t\t \r\n\t\t\r\n\t\t\tX_Name\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tLastConnectionError\r\n\t\t\tstring\r\n\t\t\tERROR_NONE\r\n\t\t\t\r\n\t\t\t\tERROR_NONE\r\n\t\t\t\tERROR_ISP_TIME_OUT\r\n\t\t\t\tERROR_COMMAND_ABORTED\r\n\t\t\t\tERROR_NOT_ENABLED_FOR_INTERNET\r\n\t\t\t\tERROR_BAD_PHONE_NUMBER\r\n\t\t\t\tERROR_USER_DISCONNECT\r\n\t\t\t\tERROR_ISP_DISCONNECT\r\n\t\t\t\tERROR_IDLE_DISCONNECT\r\n\t\t\t\tERROR_FORCED_DISCONNECT\r\n\t\t\t\tERROR_SERVER_OUT_OF_RESOURCES\r\n\t\t\t\tERROR_RESTRICTED_LOGON_HOURS\r\n\t\t\t\tERROR_ACCOUNT_DISABLED\r\n\t\t\t\tERROR_ACCOUNT_EXPIRED\r\n\t\t\t\tERROR_PASSWORD_EXPIRED\r\n\t\t\t\tERROR_AUTHENTICATION_FAILURE\r\n\t\t\t\tERROR_NO_DIALTONE\r\n\t\t\t\tERROR_NO_CARRIER\r\n\t\t\t\tERROR_NO_ANSWER\r\n\t\t\t\tERROR_LINE_BUSY\r\n\t\t\t\tERROR_UNSUPPORTED_BITSPERSECOND\r\n\t\t\t\tERROR_TOO_MANY_LINE_ERRORS\r\n\t\t\t\tERROR_IP_CONFIGURATION\r\n\t\t\t\tERROR_UNKNOWN\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tExternalIPAddress\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tRemoteHost\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tExternalPort\r\n\t\t\tui2\r\n\t\t\r\n\t\t\r\n\t\t\tInternalPort\r\n\t\t\tui2\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingProtocol\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tTCP\r\n\t\t\t\tUDP\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tInternalClient\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingDescription\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingEnabled\r\n\t\t\tboolean\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingLeaseDuration\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingNumberOfEntries\r\n\t\t\tui2\r\n\t\t\r\n\t\r\n\r\n'},
+ 'services': {'/OSInfo.xml': OrderedDict([('serviceType', 'urn:schemas-microsoft-com:service:OSInfo:1'),
+ ('serviceId', 'urn:microsoft-com:serviceId:OSInfo1'),
+ ('controlURL', '/soap.cgi?service=OSInfo1'),
+ ('eventSubURL', '/gena.cgi?service=OSInfo1'),
+ ('SCPDURL', '/OSInfo.xml')]), '/Layer3Forwarding.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:Layer3Forwarding:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:L3Forwarding1'), ('controlURL', '/soap.cgi?service=L3Forwarding1'),
+ ('eventSubURL', '/gena.cgi?service=L3Forwarding1'), ('SCPDURL', '/Layer3Forwarding.xml')]),
+ '/WANCommonInterfaceConfig.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANCommonIFC1'),
+ ('controlURL', '/soap.cgi?service=WANCommonIFC1'),
+ ('eventSubURL', '/gena.cgi?service=WANCommonIFC1'),
+ ('SCPDURL', '/WANCommonInterfaceConfig.xml')]), '/WANEthernetLinkConfig.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANEthernetLinkConfig:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANEthLinkC1'),
+ ('controlURL', '/soap.cgi?service=WANEthLinkC1'), ('eventSubURL', '/gena.cgi?service=WANEthLinkC1'),
+ ('SCPDURL', '/WANEthernetLinkConfig.xml')]), '/WANIPConnection.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANIPConnection:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANIPConn1'), ('controlURL', '/soap.cgi?service=WANIPConn1'),
+ ('eventSubURL', '/gena.cgi?service=WANIPConn1'), ('SCPDURL', '/WANIPConnection.xml')])},
+ 'm_search_args': OrderedDict([('HOST', '239.255.255.250:1900'), ('MAN', 'ssdp:discover'), ('MX', 1),
+ ('ST', 'urn:schemas-upnp-org:device:WANDevice:1')]), 'reply': OrderedDict(
+ [('CACHE_CONTROL', 'max-age=1800'), ('LOCATION', 'http://10.0.0.1:49152/InternetGatewayDevice.xml'),
+ ('SERVER', 'Linux, UPnP/1.0, DIR-890L Ver 1.20'), ('ST', 'urn:schemas-upnp-org:device:WANDevice:1'),
+ ('USN', 'uuid:11111111-2222-3333-4444-555555555555::urn:schemas-upnp-org:device:WANDevice:1')]),
+ 'soap_port': 49152,
+ 'registered_soap_commands': {'GetGenericPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'GetSpecificPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'AddPortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'DeletePortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'GetExternalIPAddress': 'urn:schemas-upnp-org:service:WANIPConnection:1'},
+ 'unsupported_soap_commands': {
+ 'urn:schemas-upnp-org:service:Layer3Forwarding:1': ['GetDefaultConnectionService',
+ 'SetDefaultConnectionService'],
+ 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1': ['GetCommonLinkProperties', 'GetTotalBytesSent',
+ 'GetTotalBytesReceived', 'GetTotalPacketsSent',
+ 'GetTotalPacketsReceived',
+ 'X_GetICSStatistics'],
+ 'urn:schemas-upnp-org:service:WANEthernetLinkConfig:1': ['GetEthernetLinkStatus'],
+ 'urn:schemas-upnp-org:service:WANIPConnection:1': ['SetConnectionType', 'GetConnectionTypeInfo',
+ 'RequestConnection', 'ForceTermination',
+ 'GetStatusInfo', 'GetNATRSIPStatus']},
+ 'soap_requests': []}
+
client_address = "10.0.0.2"
- soap_port = 49152
- m_search_args = OrderedDict([
- ("HOST", "239.255.255.250:1900"),
- ("MAN", "ssdp:discover"),
- ("MX", 1),
- ("ST", "urn:schemas-upnp-org:device:WANDevice:1")
- ])
-
- reply = SSDPDatagram("OK", OrderedDict([
- ("CACHE_CONTROL", "max-age=1800"),
- ("LOCATION", "http://10.0.0.1:49152/InternetGatewayDevice.xml"),
- ("SERVER", "Linux, UPnP/1.0, DIR-890L Ver 1.20"),
- ("ST", "urn:schemas-upnp-org:device:WANDevice:1"),
- ("USN", "uuid:11111111-2222-3333-4444-555555555555::urn:schemas-upnp-org:device:WANDevice:1")
- ]))
-
- replies = {
- b'GET /InternetGatewayDevice.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 3921\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\thttp://10.0.0.1:49152\n\t\n\t\turn:schemas-upnp-org:device:InternetGatewayDevice:1\n\t\tWireless Broadband Router\n\t\tD-Link Corporation\n\t\thttp://www.dlink.com\n\t\tD-Link Router\n\t\tD-Link Router\n\t\tDIR-890L\n\t\thttp://www.dlink.com\n\t\t120\n\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\n\t\t\t\n\t\t\t\timage/gif\n\t\t\t\t118\n\t\t\t\t119\n\t\t\t\t8\n\t\t\t\t/ligd.gif\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\turn:schemas-microsoft-com:service:OSInfo:1\n\t\t\t\turn:microsoft-com:serviceId:OSInfo1\n\t\t\t\t/soap.cgi?service=OSInfo1\n\t\t\t\t/gena.cgi?service=OSInfo1\n\t\t\t\t/OSInfo.xml\n\t\t\t\n\t\t\t\n\t\t\t\turn:schemas-upnp-org:service:Layer3Forwarding:1\n\t\t\t\turn:upnp-org:serviceId:L3Forwarding1\n\t\t\t\t/soap.cgi?service=L3Forwarding1\n\t\t\t\t/gena.cgi?service=L3Forwarding1\n\t\t\t\t/Layer3Forwarding.xml\n\t\t\t\n\t\t\n\t\t\n\t\t\t\n\t\t\t\turn:schemas-upnp-org:device:WANDevice:1\n\t\t\t\tWANDevice\n\t\t\t\tD-Link\n\t\t\t\thttp://www.dlink.com\n\t\t\t\tWANDevice\n\t\t\t\tDIR-890L\n\t\t\t\t1\n\t\t\t\thttp://www.dlink.com\n\t\t\t\t120\n\t\t\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\turn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\n\t\t\t\t\t\turn:upnp-org:serviceId:WANCommonIFC1\n\t\t\t\t\t\t/soap.cgi?service=WANCommonIFC1\n\t\t\t\t\t\t/gena.cgi?service=WANCommonIFC1\n\t\t\t\t\t\t/WANCommonInterfaceConfig.xml\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\turn:schemas-upnp-org:device:WANConnectionDevice:1\n\t\t\t\t\t\tWANConnectionDevice\n\t\t\t\t\t\tD-Link\n\t\t\t\t\t\thttp://www.dlink.com\n\t\t\t\t\t\tWanConnectionDevice\n\t\t\t\t\t\tDIR-890L\n\t\t\t\t\t\t1\n\t\t\t\t\t\thttp://www.dlink.com\n\t\t\t\t\t\t120\n\t\t\t\t\t\tuuid:11111111-2222-3333-4444-555555555555\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\turn:schemas-upnp-org:service:WANEthernetLinkConfig:1\n\t\t\t\t\t\t\t\turn:upnp-org:serviceId:WANEthLinkC1\n\t\t\t\t\t\t\t\t/soap.cgi?service=WANEthLinkC1\n\t\t\t\t\t\t\t\t/gena.cgi?service=WANEthLinkC1\n\t\t\t\t\t\t\t\t/WANEthernetLinkConfig.xml\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\turn:schemas-upnp-org:service:WANIPConnection:1\n\t\t\t\t\t\t\t\turn:upnp-org:serviceId:WANIPConn1\n\t\t\t\t\t\t\t\t/soap.cgi?service=WANIPConn1\n\t\t\t\t\t\t\t\t/gena.cgi?service=WANIPConn1\n\t\t\t\t\t\t\t\t/WANIPConnection.xml\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\thttp://10.0.0.1\n\t\n\n",
- b'GET /OSInfo.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 219\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\n\t\n\t\n\n",
- b'GET /Layer3Forwarding.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 920\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\t\n\t\t\tGetDefaultConnectionService\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewDefaultConnectionService\n\t\t\t\t\tout\n\t\t\t\t\tDefaultConnectionService\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t\n\t\t\tSetDefaultConnectionService\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewDefaultConnectionService\n\t\t\t\t\tin\n\t\t\t\t\tDefaultConnectionService\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\tDefaultConnectionService\n\t\t\tstring\n\t\t\n\t\n\n",
- b'GET /WANCommonInterfaceConfig.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 5343\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\r\n\r\n\t\r\n\t\t1\r\n\t\t0\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tGetCommonLinkProperties\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewWANAccessType\r\n\t\t\t\t\tout\r\n\t\t\t\t\tWANAccessType\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLayer1UpstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1UpstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLayer1DownstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPhysicalLinkStatus\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPhysicalLinkStatus\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalBytesSent\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalBytesSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalBytesReceived\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalBytesReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalPacketsSent\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalPacketsSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetTotalPacketsReceived\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewTotalPacketsReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tX_GetICSStatistics\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesSent\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalBytesReceived\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsSent\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\tout\r\n\t\t\t\t\tTotalPacketsReceived\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tUptime\r\n\t\t\t\t\tout\r\n\t\t\t\t\tX_Uptime\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tWANAccessType\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tDSL\r\n\t\t\t\tPOTS\r\n\t\t\t\tCable\r\n\t\t\t\tEthernet\r\n\t\t\t\tOther\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tLayer1UpstreamMaxBitRate\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tLayer1DownstreamMaxBitRate\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tPhysicalLinkStatus\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tUp\r\n\t\t\t\tDown\r\n\t\t\t\tInitializing\r\n\t\t\t\tUnavailable\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tWANAccessProvider\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tMaximumActiveConnections\r\n\t\t\tui2\r\n\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tTotalBytesSent\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalBytesReceived\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalPacketsSent\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tTotalPacketsReceived\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tX_PersonalFirewallEnabled\r\n\t\t\tboolean\r\n\t\t\r\n\t\t\r\n\t\t\tX_Uptime\r\n\t\t\tui4\r\n\t\t\r\n\t\r\n\r\n",
- b'GET /WANEthernetLinkConfig.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 773\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\n\n\t\n\t\t1\n\t\t0\n\t\n\t\n\t\t\n\t\t\tGetEthernetLinkStatus\n\t\t\t\n\t\t\t\t\n\t\t\t\t\tNewEthernetLinkStatus\n\t\t\t\t\tout\n\t\t\t\t\tEthernetLinkStatus\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t\n\t\t\tEthernetLinkStatus\n\t\t\tstring\n\t\t\t\n\t\t\t\tUp\n\t\t\t\tDown\n\t\t\t\tUnavailable\n\t\t\t\n\t\t\n\t\n\n",
- b'GET /WANIPConnection.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 10.0.0.1\r\nConnection: Close\r\n\r\n': b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Thu, 11 Oct 2018 22:16:16 GMT\r\nContent-Type: text/xml\r\nContent-Length: 12078\r\nLast-Modified: Thu, 09 Aug 2018 12:41:07 GMT\r\nConnection: close\r\n\r\n\r\n\r\n\t\r\n\t\t1\r\n\t\t0\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tSetConnectionType\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionType\r\n\t\t\t\t\tin\r\n\t\t\t\t\tConnectionType\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t \r\n\t\t\r\n\t\t\tGetConnectionTypeInfo\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionType\r\n\t\t\t\t\tout\r\n\t\t\t\t\tConnectionType\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPossibleConnectionTypes\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPossibleConnectionTypes\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tRequestConnection\r\n\t\t\r\n\t\t\r\n\t\t\tForceTermination\r\n\t\t\r\n\t\t\r\n\t\t\tGetStatusInfo\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewConnectionStatus\r\n\t\t\t\t\tout\r\n\t\t\t\t\tConnectionStatus\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLastConnectionError\r\n\t\t\t\t\tout\r\n\t\t\t\t\tLastConnectionError\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewUptime\r\n\t\t\t\t\tout\r\n\t\t\t\t\tUptime\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetNATRSIPStatus\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRSIPAvailable\r\n\t\t\t\t\tout\r\n\t\t\t\t\tRSIPAvailable\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewNATEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tNATEnabled\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetGenericPortMappingEntry\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingIndex\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingNumberOfEntries\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tout\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetSpecificPortMappingEntry\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tout\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tout\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tAddPortMapping\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tInternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewInternalClient\r\n\t\t\t\t\tin\r\n\t\t\t\t\tInternalClient\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewEnabled\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingEnabled\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewPortMappingDescription\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingDescription\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewLeaseDuration\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingLeaseDuration\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tDeletePortMapping\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewRemoteHost\r\n\t\t\t\t\tin\r\n\t\t\t\t\tRemoteHost\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalPort\r\n\t\t\t\t\tin\r\n\t\t\t\t\tExternalPort\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewProtocol\r\n\t\t\t\t\tin\r\n\t\t\t\t\tPortMappingProtocol\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tGetExternalIPAddress\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tNewExternalIPAddress\r\n\t\t\t\t\tout\r\n\t\t\t\t\tExternalIPAddress\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t\r\n\t\r\n\t\t\r\n\t\t\tConnectionType\r\n\t\t\tstring\r\n\t\t\tUnconfigured\r\n\t\t\r\n\t\t\r\n\t\t\tPossibleConnectionTypes\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tUnconfigured\r\n\t\t\t\tIP_Routed\r\n\t\t\t\tIP_Bridged\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tConnectionStatus\r\n\t\t\tstring\r\n\t\t\tUnconfigured\r\n\t\t\t\r\n\t\t\t\tUnconfigured\r\n\t\t\t\tConnecting\r\n\t\t\t\tAuthenticating\r\n\t\t\t\tPendingDisconnect\r\n\t\t\t\tDisconnecting\r\n\t\t\t\tDisconnected\r\n\t\t\t\tConnected\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tUptime\r\n\t\t\tui4\r\n\t\t\t0\r\n\t\t\t\r\n\t\t\t\t0\r\n\t\t\t\t\r\n\t\t\t\t1\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tRSIPAvailable\r\n\t\tboolean\r\n\t\t\t0\r\n\t\t\r\n\t\t\r\n\t\t\tNATEnabled\r\n\t\t\tboolean\r\n\t\t\t1\r\n\t\t \r\n\t\t\r\n\t\t\tX_Name\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tLastConnectionError\r\n\t\t\tstring\r\n\t\t\tERROR_NONE\r\n\t\t\t\r\n\t\t\t\tERROR_NONE\r\n\t\t\t\tERROR_ISP_TIME_OUT\r\n\t\t\t\tERROR_COMMAND_ABORTED\r\n\t\t\t\tERROR_NOT_ENABLED_FOR_INTERNET\r\n\t\t\t\tERROR_BAD_PHONE_NUMBER\r\n\t\t\t\tERROR_USER_DISCONNECT\r\n\t\t\t\tERROR_ISP_DISCONNECT\r\n\t\t\t\tERROR_IDLE_DISCONNECT\r\n\t\t\t\tERROR_FORCED_DISCONNECT\r\n\t\t\t\tERROR_SERVER_OUT_OF_RESOURCES\r\n\t\t\t\tERROR_RESTRICTED_LOGON_HOURS\r\n\t\t\t\tERROR_ACCOUNT_DISABLED\r\n\t\t\t\tERROR_ACCOUNT_EXPIRED\r\n\t\t\t\tERROR_PASSWORD_EXPIRED\r\n\t\t\t\tERROR_AUTHENTICATION_FAILURE\r\n\t\t\t\tERROR_NO_DIALTONE\r\n\t\t\t\tERROR_NO_CARRIER\r\n\t\t\t\tERROR_NO_ANSWER\r\n\t\t\t\tERROR_LINE_BUSY\r\n\t\t\t\tERROR_UNSUPPORTED_BITSPERSECOND\r\n\t\t\t\tERROR_TOO_MANY_LINE_ERRORS\r\n\t\t\t\tERROR_IP_CONFIGURATION\r\n\t\t\t\tERROR_UNKNOWN\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tExternalIPAddress\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tRemoteHost\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tExternalPort\r\n\t\t\tui2\r\n\t\t\r\n\t\t\r\n\t\t\tInternalPort\r\n\t\t\tui2\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingProtocol\r\n\t\t\tstring\r\n\t\t\t\r\n\t\t\t\tTCP\r\n\t\t\t\tUDP\r\n\t\t\t\r\n\t\t\r\n\t\t\r\n\t\t\tInternalClient\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingDescription\r\n\t\t\tstring\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingEnabled\r\n\t\t\tboolean\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingLeaseDuration\r\n\t\t\tui4\r\n\t\t\r\n\t\t\r\n\t\t\tPortMappingNumberOfEntries\r\n\t\t\tui2\r\n\t\t\r\n\t\r\n\r\n"
- }
-
- expected_commands = {
- # 'GetDefaultConnectionService': 'urn:schemas-upnp-org:service:Layer3Forwarding:1',
- # 'SetDefaultConnectionService': 'urn:schemas-upnp-org:service:Layer3Forwarding:1',
- # 'GetCommonLinkProperties': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'GetTotalBytesSent': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'GetTotalBytesReceived': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'GetTotalPacketsSent': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'GetTotalPacketsReceived': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'X_GetICSStatistics': 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1',
- # 'SetConnectionType': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- # 'GetConnectionTypeInfo': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- # 'RequestConnection': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- # 'ForceTermination': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- # 'GetStatusInfo': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- # 'GetNATRSIPStatus': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- 'GetGenericPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- 'GetSpecificPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- 'AddPortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- 'DeletePortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
- 'GetExternalIPAddress': 'urn:schemas-upnp-org:service:WANIPConnection:1'
- }
+ def setUp(self) -> None:
+ self.replies = {
+ (
+ f"GET {path} HTTP/1.1\r\n"
+ f"Accept-Encoding: gzip\r\n"
+ f"Host: {self.gateway_info['gateway_address']}\r\n"
+ f"Connection: Close\r\n"
+ f"\r\n"
+ ).encode(): xml_bytes.encode()
+ for path, xml_bytes in self.gateway_info['services_xml'].items()
+ }
+ self.replies.update({
+ (
+ f"GET /{self.gateway_info['location'].lstrip(self.gateway_info['urlBase'])} HTTP/1.1\r\n"
+ f"Accept-Encoding: gzip\r\n"
+ f"Host: {self.gateway_info['gateway_address']}\r\n"
+ f"Connection: Close\r\n"
+ f"\r\n"
+ ).encode(): self.gateway_info['gateway_xml'].encode()
+ })
+ super().setUp()
async def test_discover_gateway(self):
with self.assertRaises(UPnPError) as e1:
with mock_tcp_and_udp(self.loop):
- await Gateway.discover_gateway("10.0.0.2", "10.0.0.1", 2)
+ await Gateway.discover_gateway(self.client_address, self.gateway_info['gateway_address'], 2,
+ loop=self.loop)
with self.assertRaises(UPnPError) as e2:
with mock_tcp_and_udp(self.loop):
- await Gateway.discover_gateway("10.0.0.2", "10.0.0.1", 2, unicast=False)
- self.assertEqual(str(e1.exception), "M-SEARCH for 10.0.0.1:1900 timed out")
- self.assertEqual(str(e2.exception), "M-SEARCH for 10.0.0.1:1900 timed out")
+ await Gateway.discover_gateway(self.client_address, self.gateway_info['gateway_address'], 2,
+ unicast=False, loop=self.loop)
+ self.assertEqual(str(e1.exception), f"M-SEARCH for {self.gateway_info['gateway_address']}:1900 timed out")
+ self.assertEqual(str(e2.exception), f"M-SEARCH for {self.gateway_info['gateway_address']}:1900 timed out")
async def test_discover_commands(self):
with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
- gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address)
- await gateway.discover_commands(self.loop)
- self.assertDictEqual(self.expected_commands, gateway._registered_commands)
+ gateway = Gateway(
+ SSDPDatagram("OK", self.gateway_info['reply']), self.gateway_info['m_search_args'],
+ self.client_address, self.gateway_info['gateway_address'], loop=self.loop
+ )
+ await gateway.discover_commands()
+ self.assertDictEqual(self.gateway_info['registered_soap_commands'], gateway._registered_commands)
+ self.assertDictEqual(gateway.debug_gateway(), self.gateway_info)
class TestDiscoverNetgearNighthawkAC2350(TestDiscoverDLinkDIR890L):
- gateway_address = "192.168.0.1"
- client_address = "192.168.0.6"
- soap_port = 5555
-
- m_search_args = OrderedDict([
- ("HOST", "239.255.255.250:1900"),
- ("MAN", "\"ssdp:discover\""),
- ("MX", 1),
- ("ST", "upnp:rootdevice")
- ])
-
- reply = SSDPDatagram("OK", OrderedDict([
- ("CACHE_CONTROL", "max-age=1800"),
- ("ST", "upnp:rootdevice"),
- ("USN", "uuid:11111111-2222-3333-4444-555555555555::upnp:rootdevice"),
- ("Server", "R7500v2 UPnP/1.0 miniupnpd/1.0"),
- ("Location", "http://192.168.0.1:5555/rootDesc.xml"),
- ]))
-
- replies = {
- b'GET /rootDesc.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 192.168.0.1\r\nConnection: Close\r\n\r\n': b'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 3720\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10http://192.168.0.1:5555VEN_01f2&DEV_0018&REV_02 VEN_01f2&DEV_8000&SUBSYS_01&REV_01 VEN_01f2&DEV_8000&REV_01 VEN_0033&DEV_0008&REV_01urn:schemas-upnp-org:device:InternetGatewayDevice:1NetworkInfrastructure.RouterNetwork.Router.Wirelessurn:schemas-upnp-org:device:InternetGatewayDevice:1R7500v2 (Gateway)NETGEAR, Inc.http://www.netgear.comNETGEAR R7500v2 NETGEAR Nighthawk X4 AC2350 Smart WiFi RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.com/home/products/wirelessroutersv1uuid:11111111-2222-3333-4444-555555555555606449084528urn:schemas-upnp-org:service:Layer3Forwarding:1urn:upnp-org:serviceId:L3Forwarding1/ctl/L3Forwarding/evt/L3Forwarding/Layer3F.xmlurn:schemas-upnp-org:device:WANDevice:1WAN DeviceNETGEARhttp://www.netgear.comWAN Device on NETGEAR R7500v2 Wireless RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.comv1uuid:11111111-2222-3333-4444-5555555555551234567890aburn:schemas-upnp-org:service:WANCommonInterfaceConfig:1urn:upnp-org:serviceId:WANCommonIFC1/ctl/CommonIfCfg/evt/CommonIfCfg/WANCfg.xmlurn:schemas-upnp-org:device:WANConnectionDevice:1WAN Connection DeviceNETGEARhttp://www.netgear.comWANConnectionDevice on NETGEAR R7500v2 Wireless RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.comv1uuid:4d696e69-444c-164e-9d44-b0b98a4cd3c31234567890aburn:schemas-upnp-org:service:WANEthernetLinkConfig:1urn:upnp-org:serviceId:WANEthLinkC1/ctl/WanEth/evt/WanEth/WanEth.xmlurn:schemas-upnp-org:service:WANIPConnection:1urn:upnp-org:serviceId:WANIPConn1/ctl/IPConn/evt/IPConn/WANIPCn.xmlhttp://www.routerlogin.net',
- b'GET /Layer3F.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 192.168.0.1\r\nConnection: Close\r\n\r\n': b'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 794\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10SetDefaultConnectionServiceNewDefaultConnectionServiceinDefaultConnectionServiceGetDefaultConnectionServiceNewDefaultConnectionServiceoutDefaultConnectionServiceDefaultConnectionServicestring',
- b'GET /WANCfg.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 192.168.0.1\r\nConnection: Close\r\n\r\n': b'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 2942\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10GetCommonLinkPropertiesNewWANAccessTypeoutWANAccessTypeNewLayer1UpstreamMaxBitRateoutLayer1UpstreamMaxBitRateNewLayer1DownstreamMaxBitRateoutLayer1DownstreamMaxBitRateNewPhysicalLinkStatusoutPhysicalLinkStatusGetTotalBytesSentNewTotalBytesSentoutTotalBytesSentGetTotalBytesReceivedNewTotalBytesReceivedoutTotalBytesReceivedGetTotalPacketsSentNewTotalPacketsSentoutTotalPacketsSentGetTotalPacketsReceivedNewTotalPacketsReceivedoutTotalPacketsReceivedWANAccessTypestringDSLPOTSCableEthernetLayer1UpstreamMaxBitRateui4Layer1DownstreamMaxBitRateui4PhysicalLinkStatusstringUpDownInitializingUnavailableTotalBytesSentui4TotalBytesReceivedui4TotalPacketsSentui4TotalPacketsReceivedui4',
- b'GET /WanEth.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 192.168.0.1\r\nConnection: Close\r\n\r\n': b'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 711\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10GetEthernetLinkStatusNewEthernetLinkStatusoutEthernetLinkStatusEthernetLinkStatusstringUpDownInitializingUnavailable',
- b'GET /WANIPCn.xml HTTP/1.1\r\nAccept-Encoding: gzip\r\nHost: 192.168.0.1\r\nConnection: Close\r\n\r\n': b'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 8400\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10AddPortMappingNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolNewInternalPortinInternalPortNewInternalClientinInternalClientNewEnabledinPortMappingEnabledNewPortMappingDescriptioninPortMappingDescriptionNewLeaseDurationinPortMappingLeaseDurationGetExternalIPAddressNewExternalIPAddressoutExternalIPAddressDeletePortMappingNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolSetConnectionTypeNewConnectionTypeinConnectionTypeGetConnectionTypeInfoNewConnectionTypeoutConnectionTypeNewPossibleConnectionTypesoutPossibleConnectionTypesRequestConnectionForceTerminationGetStatusInfoNewConnectionStatusoutConnectionStatusNewLastConnectionErroroutLastConnectionErrorNewUptimeoutUptimeGetNATRSIPStatusNewRSIPAvailableoutRSIPAvailableNewNATEnabledoutNATEnabledGetGenericPortMappingEntryNewPortMappingIndexinPortMappingNumberOfEntriesNewRemoteHostoutRemoteHostNewExternalPortoutExternalPortNewProtocoloutPortMappingProtocolNewInternalPortoutInternalPortNewInternalClientoutInternalClientNewEnabledoutPortMappingEnabledNewPortMappingDescriptionoutPortMappingDescriptionNewLeaseDurationoutPortMappingLeaseDurationGetSpecificPortMappingEntryNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolNewInternalPortoutInternalPortNewInternalClientoutInternalClientNewEnabledoutPortMappingEnabledNewPortMappingDescriptionoutPortMappingDescriptionNewLeaseDurationoutPortMappingLeaseDurationConnectionTypestringPossibleConnectionTypesstringUnconfiguredIP_RoutedIP_BridgedConnectionStatusstringUnconfiguredConnectingConnectedPendingDisconnectDisconnectingDisconnectedUptimeui4LastConnectionErrorstringERROR_NONERSIPAvailablebooleanNATEnabledbooleanExternalIPAddressstringPortMappingNumberOfEntriesui2PortMappingEnabledbooleanPortMappingLeaseDurationui4RemoteHoststringExternalPortui2InternalPortui2PortMappingProtocolstringTCPUDPInternalClientstringPortMappingDescriptionstring'
- }
-
- expected_commands = {
- # "SetDefaultConnectionService": "urn:schemas-upnp-org:service:Layer3Forwarding:1",
- # "GetDefaultConnectionService": "urn:schemas-upnp-org:service:Layer3Forwarding:1",
- # "GetCommonLinkProperties": "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
- # "GetTotalBytesSent": "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
- # "GetTotalBytesReceived": "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
- # "GetTotalPacketsSent": "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
- # "GetTotalPacketsReceived": "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
- "AddPortMapping": "urn:schemas-upnp-org:service:WANIPConnection:1",
- "GetExternalIPAddress": "urn:schemas-upnp-org:service:WANIPConnection:1",
- "DeletePortMapping": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "SetConnectionType": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "GetConnectionTypeInfo": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "RequestConnection": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "ForceTermination": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "GetStatusInfo": "urn:schemas-upnp-org:service:WANIPConnection:1",
- # "GetNATRSIPStatus": "urn:schemas-upnp-org:service:WANIPConnection:1",
- "GetGenericPortMappingEntry": "urn:schemas-upnp-org:service:WANIPConnection:1",
- "GetSpecificPortMappingEntry": "urn:schemas-upnp-org:service:WANIPConnection:1"
- }
+ gateway_info = {'manufacturer_string': 'NETGEAR NETGEAR Nighthawk X4 AC2350 Smart WiFi Router',
+ 'gateway_address': '192.168.0.1', 'server': 'R7500v2 UPnP/1.0 miniupnpd/1.0',
+ 'urlBase': 'http://192.168.0.1:5555', 'location': 'http://192.168.0.1:5555/rootDesc.xml',
+ 'specVersion': {'major': '1', 'minor': '0'},
+ 'usn': 'uuid:11111111-2222-3333-4444-555555555555::upnp:rootdevice', 'urn': 'upnp:rootdevice',
+ 'gateway_xml': 'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 3720\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10http://192.168.0.1:5555VEN_01f2&DEV_0018&REV_02 VEN_01f2&DEV_8000&SUBSYS_01&REV_01 VEN_01f2&DEV_8000&REV_01 VEN_0033&DEV_0008&REV_01urn:schemas-upnp-org:device:InternetGatewayDevice:1NetworkInfrastructure.RouterNetwork.Router.Wirelessurn:schemas-upnp-org:device:InternetGatewayDevice:1R7500v2 (Gateway)NETGEAR, Inc.http://www.netgear.comNETGEAR R7500v2 NETGEAR Nighthawk X4 AC2350 Smart WiFi RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.com/home/products/wirelessroutersv1uuid:11111111-2222-3333-4444-555555555555606449084528urn:schemas-upnp-org:service:Layer3Forwarding:1urn:upnp-org:serviceId:L3Forwarding1/ctl/L3Forwarding/evt/L3Forwarding/Layer3F.xmlurn:schemas-upnp-org:device:WANDevice:1WAN DeviceNETGEARhttp://www.netgear.comWAN Device on NETGEAR R7500v2 Wireless RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.comv1uuid:11111111-2222-3333-4444-5555555555551234567890aburn:schemas-upnp-org:service:WANCommonInterfaceConfig:1urn:upnp-org:serviceId:WANCommonIFC1/ctl/CommonIfCfg/evt/CommonIfCfg/WANCfg.xmlurn:schemas-upnp-org:device:WANConnectionDevice:1WAN Connection DeviceNETGEARhttp://www.netgear.comWANConnectionDevice on NETGEAR R7500v2 Wireless RouterNETGEAR Nighthawk X4 AC2350 Smart WiFi RouterR7500v2http://www.netgear.comv1uuid:4d696e69-444c-164e-9d44-b0b98a4cd3c31234567890aburn:schemas-upnp-org:service:WANEthernetLinkConfig:1urn:upnp-org:serviceId:WANEthLinkC1/ctl/WanEth/evt/WanEth/WanEth.xmlurn:schemas-upnp-org:service:WANIPConnection:1urn:upnp-org:serviceId:WANIPConn1/ctl/IPConn/evt/IPConn/WANIPCn.xmlhttp://www.routerlogin.net',
+ 'services_xml': {
+ '/Layer3F.xml': 'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 794\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10SetDefaultConnectionServiceNewDefaultConnectionServiceinDefaultConnectionServiceGetDefaultConnectionServiceNewDefaultConnectionServiceoutDefaultConnectionServiceDefaultConnectionServicestring',
+ '/WANCfg.xml': 'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 2942\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10GetCommonLinkPropertiesNewWANAccessTypeoutWANAccessTypeNewLayer1UpstreamMaxBitRateoutLayer1UpstreamMaxBitRateNewLayer1DownstreamMaxBitRateoutLayer1DownstreamMaxBitRateNewPhysicalLinkStatusoutPhysicalLinkStatusGetTotalBytesSentNewTotalBytesSentoutTotalBytesSentGetTotalBytesReceivedNewTotalBytesReceivedoutTotalBytesReceivedGetTotalPacketsSentNewTotalPacketsSentoutTotalPacketsSentGetTotalPacketsReceivedNewTotalPacketsReceivedoutTotalPacketsReceivedWANAccessTypestringDSLPOTSCableEthernetLayer1UpstreamMaxBitRateui4Layer1DownstreamMaxBitRateui4PhysicalLinkStatusstringUpDownInitializingUnavailableTotalBytesSentui4TotalBytesReceivedui4TotalPacketsSentui4TotalPacketsReceivedui4',
+ '/WanEth.xml': 'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 711\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10GetEthernetLinkStatusNewEthernetLinkStatusoutEthernetLinkStatusEthernetLinkStatusstringUpDownInitializingUnavailable',
+ '/WANIPCn.xml': 'HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset="utf-8"\r\nConnection: close\r\nContent-Length: 8400\r\nServer: R7500v2 UPnP/1.0 miniupnpd/1.0\r\nExt: \r\nContent-Language: en-US\r\n\r\n\n10AddPortMappingNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolNewInternalPortinInternalPortNewInternalClientinInternalClientNewEnabledinPortMappingEnabledNewPortMappingDescriptioninPortMappingDescriptionNewLeaseDurationinPortMappingLeaseDurationGetExternalIPAddressNewExternalIPAddressoutExternalIPAddressDeletePortMappingNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolSetConnectionTypeNewConnectionTypeinConnectionTypeGetConnectionTypeInfoNewConnectionTypeoutConnectionTypeNewPossibleConnectionTypesoutPossibleConnectionTypesRequestConnectionForceTerminationGetStatusInfoNewConnectionStatusoutConnectionStatusNewLastConnectionErroroutLastConnectionErrorNewUptimeoutUptimeGetNATRSIPStatusNewRSIPAvailableoutRSIPAvailableNewNATEnabledoutNATEnabledGetGenericPortMappingEntryNewPortMappingIndexinPortMappingNumberOfEntriesNewRemoteHostoutRemoteHostNewExternalPortoutExternalPortNewProtocoloutPortMappingProtocolNewInternalPortoutInternalPortNewInternalClientoutInternalClientNewEnabledoutPortMappingEnabledNewPortMappingDescriptionoutPortMappingDescriptionNewLeaseDurationoutPortMappingLeaseDurationGetSpecificPortMappingEntryNewRemoteHostinRemoteHostNewExternalPortinExternalPortNewProtocolinPortMappingProtocolNewInternalPortoutInternalPortNewInternalClientoutInternalClientNewEnabledoutPortMappingEnabledNewPortMappingDescriptionoutPortMappingDescriptionNewLeaseDurationoutPortMappingLeaseDurationConnectionTypestringPossibleConnectionTypesstringUnconfiguredIP_RoutedIP_BridgedConnectionStatusstringUnconfiguredConnectingConnectedPendingDisconnectDisconnectingDisconnectedUptimeui4LastConnectionErrorstringERROR_NONERSIPAvailablebooleanNATEnabledbooleanExternalIPAddressstringPortMappingNumberOfEntriesui2PortMappingEnabledbooleanPortMappingLeaseDurationui4RemoteHoststringExternalPortui2InternalPortui2PortMappingProtocolstringTCPUDPInternalClientstringPortMappingDescriptionstring'},
+ 'services': {'/Layer3F.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:Layer3Forwarding:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:L3Forwarding1'), ('controlURL', '/ctl/L3Forwarding'),
+ ('eventSubURL', '/evt/L3Forwarding'), ('SCPDURL', '/Layer3F.xml')]),
+ '/WANCfg.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANCommonIFC1'),
+ ('controlURL', '/ctl/CommonIfCfg'), ('eventSubURL', '/evt/CommonIfCfg'),
+ ('SCPDURL', '/WANCfg.xml')]), '/WanEth.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANEthernetLinkConfig:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANEthLinkC1'), ('controlURL', '/ctl/WanEth'),
+ ('eventSubURL', '/evt/WanEth'), ('SCPDURL', '/WanEth.xml')]), '/WANIPCn.xml': OrderedDict(
+ [('serviceType', 'urn:schemas-upnp-org:service:WANIPConnection:1'),
+ ('serviceId', 'urn:upnp-org:serviceId:WANIPConn1'), ('controlURL', '/ctl/IPConn'),
+ ('eventSubURL', '/evt/IPConn'), ('SCPDURL', '/WANIPCn.xml')])},
+ 'm_search_args': OrderedDict(
+ [('HOST', '239.255.255.250:1900'), ('MAN', '"ssdp:discover"'), ('MX', 1),
+ ('ST', 'upnp:rootdevice')]), 'reply': OrderedDict(
+ [('CACHE_CONTROL', 'max-age=1800'), ('ST', 'upnp:rootdevice'),
+ ('USN', 'uuid:11111111-2222-3333-4444-555555555555::upnp:rootdevice'),
+ ('Server', 'R7500v2 UPnP/1.0 miniupnpd/1.0'), ('Location', 'http://192.168.0.1:5555/rootDesc.xml')]),
+ 'soap_port': 5555,
+ 'registered_soap_commands': {'AddPortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'GetExternalIPAddress': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'DeletePortMapping': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'GetGenericPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1',
+ 'GetSpecificPortMappingEntry': 'urn:schemas-upnp-org:service:WANIPConnection:1'},
+ 'unsupported_soap_commands': {
+ 'urn:schemas-upnp-org:service:Layer3Forwarding:1': ['SetDefaultConnectionService',
+ 'GetDefaultConnectionService'],
+ 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1': ['GetCommonLinkProperties',
+ 'GetTotalBytesSent',
+ 'GetTotalBytesReceived',
+ 'GetTotalPacketsSent',
+ 'GetTotalPacketsReceived'],
+ 'urn:schemas-upnp-org:service:WANEthernetLinkConfig:1': ['GetEthernetLinkStatus'],
+ 'urn:schemas-upnp-org:service:WANIPConnection:1': ['SetConnectionType', 'GetConnectionTypeInfo',
+ 'RequestConnection', 'ForceTermination',
+ 'GetStatusInfo', 'GetNATRSIPStatus']},
+ 'soap_requests': []}
diff --git a/tests/test_upnp.py b/tests/test_upnp.py
index bd93570..267d9fe 100644
--- a/tests/test_upnp.py
+++ b/tests/test_upnp.py
@@ -44,8 +44,46 @@ class TestGetExternalIPAddress(UPnPCommandTestCase):
async def test_get_external_ip(self):
with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
- gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address)
- await gateway.discover_commands(self.loop)
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
+ upnp = UPnP(self.client_address, self.gateway_address, gateway)
+ external_ip = await upnp.get_external_ip()
+ self.assertEqual("11.222.3.44", external_ip)
+
+
+class TestMalformedGetExternalIPAddressResponse(UPnPCommandTestCase):
+ client_address = '11.2.3.222'
+ get_ip_request = b'POST /soap.cgi?service=WANIPConn1 HTTP/1.1\r\nHost: 11.2.3.4\r\nUser-Agent: python3/aioupnp, UPnP/1.0, MiniUPnPc/1.9\r\nContent-Length: 285\r\nContent-Type: text/xml\r\nSOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"\r\nConnection: Close\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\r\n\r\n'
+
+ async def test_response_key_mismatch(self):
+ self.replies.update({self.get_ip_request: b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Wed, 22 May 2019 03:25:57 GMT\r\nConnection: close\r\nCONTENT-TYPE: text/xml; charset=\"utf-8\"\r\nCONTENT-LENGTH: 333 \r\nEXT:\r\n\r\n\n\n\t\n\t\t\n"
+ b"11.222.3.44\n\n\t\n\n"})
+ self.addCleanup(self.replies.pop, self.get_ip_request)
+ with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
+ upnp = UPnP(self.client_address, self.gateway_address, gateway)
+ with self.assertRaises(UPnPError):
+ await upnp.get_external_ip()
+
+ async def test_response_key_case_sensitivity(self):
+ self.replies.update({self.get_ip_request: b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Wed, 22 May 2019 03:25:57 GMT\r\nConnection: close\r\nCONTENT-TYPE: text/xml; charset=\"utf-8\"\r\nCONTENT-LENGTH: 365 \r\nEXT:\r\n\r\n\n\n\t\n\t\t\n"
+ b"11.222.3.44\n\n\t\n\n"})
+ self.addCleanup(self.replies.pop, self.get_ip_request)
+ with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
+ upnp = UPnP(self.client_address, self.gateway_address, gateway)
+ external_ip = await upnp.get_external_ip()
+ self.assertEqual("11.222.3.44", external_ip)
+
+ async def test_non_encapsulated_single_field_response(self):
+ self.replies.update({self.get_ip_request: b"HTTP/1.1 200 OK\r\nServer: WebServer\r\nDate: Wed, 22 May 2019 03:25:57 GMT\r\nConnection: close\r\nCONTENT-TYPE: text/xml; charset=\"utf-8\"\r\nCONTENT-LENGTH: 320 \r\nEXT:\r\n\r\n\n\n\t\n\t\t\n"
+ b"11.222.3.44\n\n\t\n\n"})
+ self.addCleanup(self.replies.pop, self.get_ip_request)
+ with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
upnp = UPnP(self.client_address, self.gateway_address, gateway)
external_ip = await upnp.get_external_ip()
self.assertEqual("11.222.3.44", external_ip)
@@ -62,8 +100,8 @@ class TestGetGenericPortMappingEntry(UPnPCommandTestCase):
async def test_get_port_mapping_by_index(self):
with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
- gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address)
- await gateway.discover_commands(self.loop)
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
upnp = UPnP(self.client_address, self.gateway_address, gateway)
result = await upnp.get_port_mapping_by_index(0)
self.assertEqual(GetGenericPortMappingEntryResponse(None, 9308, 'UDP', 9308, "11.2.3.44", True,
@@ -84,8 +122,8 @@ class TestGetNextPortMapping(UPnPCommandTestCase):
async def test_get_next_mapping(self):
with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
- gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address)
- await gateway.discover_commands(self.loop)
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
upnp = UPnP(self.client_address, self.gateway_address, gateway)
ext_port = await upnp.get_next_mapping(4567, "UDP", "aioupnp test mapping")
self.assertEqual(4567, ext_port)
@@ -104,8 +142,8 @@ class TestGetSpecificPortMapping(UPnPCommandTestCase):
async def test_get_specific_port_mapping(self):
with mock_tcp_and_udp(self.loop, tcp_replies=self.replies):
- gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address)
- await gateway.discover_commands(self.loop)
+ gateway = Gateway(self.reply, self.m_search_args, self.client_address, self.gateway_address, loop=self.loop)
+ await gateway.discover_commands()
upnp = UPnP(self.client_address, self.gateway_address, gateway)
try:
await upnp.get_specific_port_mapping(1000, 'UDP')