协议-upnp

upnp访问过程

#随笔

udp访问239.255.255.250的1900端口,ssdp协议

import socket
import re

ANY = "0.0.0.0"
DES_IP = "239.255.255.250"
PORT = 1900
xml_str = b'M-SEARCH * HTTP/1.1\r\n' \
    \+ b'HOST: 239.255.255.250:1900\r\n' \
    \+ b'MAN: "ssdp:discover"\r\n' \
    \+ b'MX: 1\r\n' \
    \+ b'ST: urn:dial-multiscreen-org:service:dial:1\r\n' \
    \+ b'USER-AGENT: Google Chrome/87.0.4280.88 Windows\r\n\r\n\r\n'
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((ANY,PORT))
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
s.setsockopt(
  socket.IPPROTO_IP,
  socket.IP_ADD_MEMBERSHIP,
  socket.inet_aton(DES_IP) + socket.inet_aton(ANY)
)
s.setblocking(False)
s.sendto(xml_str,(DES_IP,PORT))
while True:
  try:
    data, address = s.recvfrom(2048)
  except Exception as e:
    pass
  else:
    print(address)
    print(data)

通过这种方式可以获取到同网下的其他upnp设备信息

('10.0.0.1', 35150)

b'NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=3600\r\nLOCATION: http://10.0.0.1:56688/rootDesc.xml\r\nSERVER: OpenWRT/OpenWrt UPnP/1.1 MiniUPnPd/2.0\r\nNT: upnp:rootdevice\r\nUSN: uuid:97fd7581-d522-415d-bfae-a51ac69e4b55::upnp:rootdevice\r\nNTS: ssdp:alive\r\nOPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01\r\n01-NLS: 1693189369\r\nBOOTID.UPNP.ORG: 1693189369\r\nCONFIGID.UPNP.ORG: 1337\r\n\r\n'

('10.0.0.1', 60956)

b'NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1801\r\nNTS: ssdp:alive\r\nLOCATION: http://10.0.0.1:49153/wps_device.xml\r\nSERVER: Unspecified, UPnP/1.0, Unspecified\r\nNT: urn:schemas-wifialliance-org:service:WFAWLANConfig:1\r\nUSN: uuid:dd20a05f-a846-47cf-b976-047083022a1d::urn:schemas-wifialliance-org:service:WFAWLANConfig:1\r\n\r\n'

现在是以10.0.0.1做演示

信息中能发现LOCATION

http://10.0.0.1:49153/wps_device.xml

http://10.0.0.1:56688/rootDesc.xml

rootDesc.xml文件

This XML file does not appear to have any style information associated with it. The document tree is shown below.

<root  xmlns="urn:schemas-upnp-org:device-1-0"  configId="1337">
  <specVersion>
​    <major>1</major>
​    <minor>1</minor>
  </specVersion>
  <device>
​    <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
​    <friendlyName>RAX40 (Gateway)</friendlyName>
​    <manufacturer>Netgear, Inc.</manufacturer>
​    <manufacturerURL>http://www.NETGEAR.com</manufacturerURL>
​    <modelDescription>OpenWRT router</modelDescription>
​    <modelName>RAX40</modelName>
​    <modelNumber>RAX40</modelNumber>
​    <modelURL>http://www.netgear.com/home</modelURL>
​    <serialNumber>5UP299WPA0C55</serialNumber>
​    <UDN>uuid:97fd7581-d522-415d-bfae-a51ac69e4b55</UDN>
​    <serviceList>
​      <service>
​        <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
​        <serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
​        <SCPDURL>/L3F.xml</SCPDURL>
​        <controlURL>/ctl/L3F</controlURL>
​        <eventSubURL>/evt/L3F</eventSubURL>
​      </service>
​    </serviceList>
​    <deviceList>
​      <device>
​        <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
​        <friendlyName>WANDevice</friendlyName>
​        <manufacturer>MiniUPnP</manufacturer>
​        <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL>
​        <modelDescription>WAN Device</modelDescription>
​        <modelName>WAN Device</modelName>
​        <modelNumber>20220318</modelNumber>
​        <modelURL>http://miniupnp.free.fr/</modelURL>
​        <serialNumber>5UP299WPA0C55</serialNumber>
​        <UDN>uuid:97fd7581-d522-415d-bfae-a51ac69e4b56</UDN>
​        <UPC>000000000000</UPC>
​        <serviceList>
​          <service>
​            <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
​            <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
​            <SCPDURL>/WANCfg.xml</SCPDURL>
​            <controlURL>/ctl/CmnIfCfg</controlURL>
​            <eventSubURL>/evt/CmnIfCfg</eventSubURL>
​          </service>
​        </serviceList>
​        <deviceList>
​          <device>
​            <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
​            <friendlyName>WANConnectionDevice</friendlyName>
​            <manufacturer>MiniUPnP</manufacturer>
​            <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL>
​            <modelDescription>MiniUPnP daemon</modelDescription>
​            <modelName>MiniUPnPd</modelName>
​            <modelNumber>20220318</modelNumber>
​            <modelURL>http://miniupnp.free.fr/</modelURL>
​            <serialNumber>5UP299WPA0C55</serialNumber>
​            <UDN>uuid:97fd7581-d522-415d-bfae-a51ac69e4b57</UDN>
​            <UPC>000000000000</UPC>
​            <serviceList>
​              <service>
​                <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
​                <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
​                <SCPDURL>/WANIPCn.xml</SCPDURL>
​                <controlURL>/ctl/IPConn</controlURL>
​                <eventSubURL>/evt/IPConn</eventSubURL>
​              </service>
​            </serviceList>
​          </device>
​        </deviceList>
​      </device>
​    </deviceList>
​    <presentationURL>http://www.routerlogin.net</presentationURL>
  </device>
</root>

其中可以获取设备名称制造商各种服务等信息

L3Forwarding1

<service>

​	<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>

​	<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>

​	<SCPDURL>/L3F.xml</SCPDURL>

​	<controlURL>/ctl/L3F</controlURL>

​	<eventSubURL>/evt/L3F</eventSubURL>

</service>

WANCommonIFC1

<service>

​	<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>

​	<serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>

​	<SCPDURL>/WANCfg.xml</SCPDURL>

​	<controlURL>/ctl/CmnIfCfg</controlURL>

​	<eventSubURL>/evt/CmnIfCfg</eventSubURL>

</service>

WANIPConn1

<service>

​	<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>

​	<serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>

​	<SCPDURL>/WANIPCn.xml</SCPDURL>

​	<controlURL>/ctl/IPConn</controlURL>

​	<eventSubURL>/evt/IPConn</eventSubURL>

</service>

通过访问SCPDURL可以获取服务的具体行为,以WANIPConn1为例

访问http://10.0.0.1:56688/WANIPCn.xml

This XML file does not appear to have any style information associated with it. The document tree is
shown below.
<scpd  xmlns="urn:schemas-upnp-org:service-1-0">
  <specVersion>
​    <major>1</major>
​    <minor>1</minor>
  </specVersion>
  <actionList>
​    <action>
​      <name>SetConnectionType</name>
​      <argumentList>
​        <argument>
​          <name>NewConnectionType</name>
​          <direction>in</direction>
​          <relatedStateVariable>ConnectionType</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>GetConnectionTypeInfo</name>
​      <argumentList>
​        <argument>
​          <name>NewConnectionType</name>
​          <direction>out</direction>
​          <relatedStateVariable>ConnectionType</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewPossibleConnectionTypes</name>
​          <direction>out</direction>
​          <relatedStateVariable>PossibleConnectionTypes</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>RequestConnection</name>
​    </action>
​    <action>
​      <name>ForceTermination</name>
​    </action>
​    <action>
​      <name>GetStatusInfo</name>
​      <argumentList>
​        <argument>
​          <name>NewConnectionStatus</name>
​          <direction>out</direction>
​          <relatedStateVariable>ConnectionStatus</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewLastConnectionError</name>
​          <direction>out</direction>
​          <relatedStateVariable>LastConnectionError</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewUptime</name>
​          <direction>out</direction>
​          <relatedStateVariable>Uptime</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>GetNATRSIPStatus</name>
​      <argumentList>
​        <argument>
​          <name>NewRSIPAvailable</name>
​          <direction>out</direction>
​          <relatedStateVariable>RSIPAvailable</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewNATEnabled</name>
​          <direction>out</direction>
​          <relatedStateVariable>NATEnabled</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>GetGenericPortMappingEntry</name>
​      <argumentList>
​        <argument>
​          <name>NewPortMappingIndex</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingNumberOfEntries</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewRemoteHost</name>
​          <direction>out</direction>
​          <relatedStateVariable>RemoteHost</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewExternalPort</name>
​          <direction>out</direction>
​          <relatedStateVariable>ExternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewProtocol</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalPort</name>
​          <direction>out</direction>
​          <relatedStateVariable>InternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalClient</name>
​          <direction>out</direction>
​          <relatedStateVariable>InternalClient</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewEnabled</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingEnabled</relatedStateVariable>
​        </argument
​        <argument>
​          <name>NewPortMappingDescription</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingDescription</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewLeaseDuration</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingLeaseDuration</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>GetSpecificPortMappingEntry</name>
​      <argumentList>
​        <argument>
​          <name>NewRemoteHost</name>
​          <direction>in</direction
​          <relatedStateVariable>RemoteHost</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewExternalPort</name>
​          <direction>in</direction>
​          <relatedStateVariable>ExternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewProtocol</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalPort</name>
​          <direction>out</direction>
​          <relatedStateVariable>InternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalClient</name>
​          <direction>out</direction>
​          <relatedStateVariable>InternalClient</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewEnabled</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingEnabled</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewPortMappingDescription</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingDescription</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewLeaseDuration</name>
​          <direction>out</direction>
​          <relatedStateVariable>PortMappingLeaseDuration</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>AddPortMapping</name>
​      <argumentList>
​        <argument>
​          <name>NewRemoteHost</name>
​          <direction>in</direction>
​          <relatedStateVariable>RemoteHost</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewExternalPort</name>
​          <direction>in</direction>
​          <relatedStateVariable>ExternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewProtocol</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalPort</name>
​          <direction>in</direction>
​          <relatedStateVariable>InternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewInternalClient</name>
​          <direction>in</direction>
​          <relatedStateVariable>InternalClient</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewEnabled</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingEnabled</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewPortMappingDescription</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingDescription</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewLeaseDuration</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingLeaseDuration</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>DeletePortMapping</name>
​      <argumentList>
​        <argument>
​          <name>NewRemoteHost</name>
​          <direction>in</direction>
​          <relatedStateVariable>RemoteHost</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewExternalPort</name>
​          <direction>in</direction>
​          <relatedStateVariable>ExternalPort</relatedStateVariable>
​        </argument>
​        <argument>
​          <name>NewProtocol</name>
​          <direction>in</direction>
​          <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
​    <action>
​      <name>GetExternalIPAddress</name>
​      <argumentList>
​        <argument>
​          <name>NewExternalIPAddress</name>
​          <direction>out</direction>
​          <relatedStateVariable>ExternalIPAddress</relatedStateVariable>
​        </argument>
​      </argumentList>
​    </action>
  </actionList>
  <serviceStateTable>
​    <stateVariable  sendEvents="no">
​      <name>ConnectionType</name>
​      <dataType>string</dataType>
​      <defaultValue>IP_Routed</defaultValue>
​    </stateVariable>
​    <stateVariable  sendEvents="yes"
​      <name>PossibleConnectionTypes</name>
​      <dataType>string</dataType>
​      <allowedValueList>
​        <allowedValue>Unconfigured</allowedValue>
​        <allowedValue>IP_Routed</allowedValue>
​        <allowedValue>IP_Bridged</allowedValue>
​      </allowedValueList>
​    </stateVariable>
​    <stateVariable  sendEvents="yes">
​      <name>ConnectionStatus</name>
​      <dataType>string</dataType>
​      <defaultValue>Unconfigured</defaultValue>
​      <allowedValueList>
​        <allowedValue>Unconfigured</allowedValue>
​        <allowedValue>Connecting</allowedValue>
​        <allowedValue>Connected</allowedValue>
​        <allowedValue>PendingDisconnect</allowedValue>
​        <allowedValue>Disconnecting</allowedValue>
​        <allowedValue>Disconnected</allowedValue>
​      </allowedValueList>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>Uptime</name>
​      <dataType>ui4</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>LastConnectionError</name>
​      <dataType>string</dataType>
​      <defaultValue>ERROR_NONE</defaultValue>
​      <allowedValueList>
​        <allowedValue>ERROR_NONE</allowedValue>
​      </allowedValueList>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>RSIPAvailable</name>
​      <dataType>boolean</dataType>
​      <defaultValue>0</defaultValue>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>NATEnabled</name>
​      <dataType>boolean</dataType>
​      <defaultValue>1</defaultValue>
​    </stateVariable>
​    <stateVariable  sendEvents="yes">
​      <name>ExternalIPAddress</name>
​      <dataType>string</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="yes">
​      <name>PortMappingNumberOfEntries</name>
​      <dataType>ui2</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>PortMappingEnabled</name>
​      <dataType>boolean</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>PortMappingLeaseDuration</name>
​      <dataType>ui4</dataType>
​      <defaultValue>3600</defaultValue>
​      <allowedValueRange>
​        <minimum>0</minimum>
​        <maximum>604800</maximum>
​      </allowedValueRange>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>RemoteHost</name>
​      <dataType>string</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>ExternalPort</name>
​      <dataType>ui2</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>InternalPort</name>
​      <dataType>ui2</dataType>
​      <allowedValueRange>
​        <minimum>1</minimum>
​        <maximum>65535</maximum>
​      </allowedValueRange>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>PortMappingProtocol</name>
​      <dataType>string</dataType>
​      <allowedValueList>
​        <allowedValue>TCP</allowedValue>
​        <allowedValue>UDP</allowedValue>
​      </allowedValueList>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>InternalClient</name>
​      <dataType>string</dataType>
​    </stateVariable>
​    <stateVariable  sendEvents="no">
​      <name>PortMappingDescription</name>
​      <dataType>string</dataType>
​    </stateVariable>
  </serviceStateTable>
</scpd>

分类统计有以下几种:

SetConnectionType,GetConnectionTypeInfo,RequestConnection,ForceTermination,GetStatusInfo,GetNATRSIPStatus,GetGenericPortMappingEntry,GetSpecificPortMappingEntry,AddPortMapping,DeletePortMapping,GetExternalIPAddress

通过构造POST请求,访问controlURL:http://10.0.0.1:56688/ctl/IPConn

POST /ctl/IPConn HTTP/1.1
Host: 10.0.0.1:56688
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"
Connection: close
Content-Length: 260

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
​	<s:Body>
​		<u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
​		</u:GetExternalIPAddress>
​	</s:Body>
</s:Envelope>

重要的有head中的SOAPAction,构造方式为serviceType#action即:urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress

post参数来源action中的direction为in的参数,如果只有out参数,即可不用构造post参数,直接获取。

in参数的数据类型在serviceStateTable中可以获取

POST /ctl/IPConn HTTP/1.1
Accept-Encoding: identity
User-Agent: Python-urllib/3.11
Host: 10.0.0.1:56688
Content-Length: 358
Content-Type: text/xml; charset="utf-8"
Soapaction: "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"
Connection: close


<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
​	<s:Body>
​		<u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
​			<NewRemoteHost>AAAA</NewRemoteHost>
​			<NewExternalPort>AAAA</NewExternalPort>
​			<NewProtocol>TCP</NewProtocol>
​		</u:DeletePortMapping>
​	</s:Body>
</s:Envelope>

通过gdb调试可以发现,可以获取到

image-20230829175431203

python中又个upnpy库,可以很方便的执行上述步骤。

python3 -m pip install upnpy

使用方式

import upnpy

\#获取upnp
upnp = upnpy.UPnP()

\#扫描,返回一个设备列表
devices = upnp.discover()

\#确定设备,两种方式都可以
devide = devices[0]
device = upnp.get_igd()

\#获取服务列表
services = device.get_services()

\#确定服务,同两种方式均可
service = services[0] 
service = device['WANPPPConnection.1'] #service id

\#获取服务的actions,返回一个action列表
service.get_actions()

\#通过service.action['name']()可以执行
service.GetExternalIPAddress() #{'NewExternalIPAddress': '10.100.40.182'}

\#通过service.action['name'].get_input_arguments()可以获取参数
service.DeletePortMapping.get_input_arguments()
\#[{'name': 'NewRemoteHost', 'data_type': 'string', 'allowed_value_list': []}, {'name': 'NewExternalPort', 'data_type': 'ui2', 'allowed_value_list': []}, {'name': 'NewProtocol', 'data_type': 'string', 'allowed_value_list': ['TCP', 'UDP']}]

\#发送服务
service.AddPortMapping(
  NewRemoteHost='',
  NewExternalPort=80,
  NewProtocol='TCP',
  NewInternalPort=8000,
  NewInternalClient='192.168.1.3',
  NewEnabled=1,
  NewPortMappingDescription='Test port mapping entry from UPnPy.',
  NewLeaseDuration=0
)

协议-upnp
http://example.com/article/697ff101.html
Author
p1yang
Posted on
August 29, 2023
Licensed under