miniupnpc fallback

This commit is contained in:
Jack Robison 2018-07-31 11:23:21 -04:00
parent 947d70c193
commit f9c6ce9bd6
No known key found for this signature in database
GPG key ID: DF25C68FE0239BB2
2 changed files with 139 additions and 3 deletions

View file

@ -1,6 +1,6 @@
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from twisted.internet import defer from twisted.internet import defer, threads
from twisted.web.client import Agent, HTTPConnectionPool from twisted.web.client import Agent, HTTPConnectionPool
import treq import treq
from treq.client import HTTPClient from treq.client import HTTPClient
@ -318,3 +318,125 @@ class SCPDCommandRunner(object):
def SetDefaultConnectionService(NewDefaultConnectionService): def SetDefaultConnectionService(NewDefaultConnectionService):
"""Returns (None)""" """Returns (None)"""
raise NotImplementedError() raise NotImplementedError()
class UPnPFallback(object):
def __init__(self):
try:
import miniupnpc
self._upnp = miniupnpc.UPnP()
self.available = True
except ImportError:
self._upnp = None
self.available = False
@defer.inlineCallbacks
def discover(self):
if not self.available:
raise NotImplementedError()
devices = yield threads.deferToThread(self._upnp.discover)
if devices:
device_url = yield threads.deferToThread(self._upnp.selectigd)
else:
device_url = None
defer.returnValue(devices > 0)
@return_types(none)
def AddPortMapping(self, NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort, NewInternalClient,
NewEnabled, NewPortMappingDescription, NewLeaseDuration):
"""Returns None"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.addportmapping, NewExternalPort, NewProtocol, NewInternalPort,
NewInternalClient, NewPortMappingDescription, NewLeaseDuration)
def GetNATRSIPStatus(self):
"""Returns (NewRSIPAvailable, NewNATEnabled)"""
raise NotImplementedError()
@return_types(none_or_str, int, str, int, str, bool, str, int)
def GetGenericPortMappingEntry(self, NewPortMappingIndex):
"""
Returns (NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort, NewInternalClient, NewEnabled,
NewPortMappingDescription, NewLeaseDuration)
"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.getgenericportmapping, NewPortMappingIndex)
@return_types(int, str, bool, str, int)
def GetSpecificPortMappingEntry(self, NewRemoteHost, NewExternalPort, NewProtocol):
"""Returns (NewInternalPort, NewInternalClient, NewEnabled, NewPortMappingDescription, NewLeaseDuration)"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.getspecificportmapping, NewExternalPort, NewProtocol)
def SetConnectionType(self, NewConnectionType):
"""Returns None"""
raise NotImplementedError()
@return_types(str)
def GetExternalIPAddress(self):
"""Returns (NewExternalIPAddress)"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.externalipaddress)
def GetConnectionTypeInfo(self):
"""Returns (NewConnectionType, NewPossibleConnectionTypes)"""
raise NotImplementedError()
@return_types(str, str, int)
def GetStatusInfo(self):
"""Returns (NewConnectionStatus, NewLastConnectionError, NewUptime)"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.statusinfo)
def ForceTermination(self):
"""Returns None"""
raise NotImplementedError()
@return_types(none)
def DeletePortMapping(self, NewRemoteHost, NewExternalPort, NewProtocol):
"""Returns None"""
if not self.available:
raise NotImplementedError()
return threads.deferToThread(self._upnp.deleteportmapping, NewExternalPort, NewProtocol)
def RequestConnection(self):
"""Returns None"""
raise NotImplementedError()
def GetCommonLinkProperties(self):
"""Returns (NewWANAccessType, NewLayer1UpstreamMaxBitRate, NewLayer1DownstreamMaxBitRate, NewPhysicalLinkStatus)"""
raise NotImplementedError()
def GetTotalBytesSent(self):
"""Returns (NewTotalBytesSent)"""
raise NotImplementedError()
def GetTotalBytesReceived(self):
"""Returns (NewTotalBytesReceived)"""
raise NotImplementedError()
def GetTotalPacketsSent(self):
"""Returns (NewTotalPacketsSent)"""
raise NotImplementedError()
def GetTotalPacketsReceived(self):
"""Returns (NewTotalPacketsReceived)"""
raise NotImplementedError()
def X_GetICSStatistics(self):
"""Returns (TotalBytesSent, TotalBytesReceived, TotalPacketsSent, TotalPacketsReceived, Layer1DownstreamMaxBitRate, Uptime)"""
raise NotImplementedError()
def GetDefaultConnectionService(self):
"""Returns (NewDefaultConnectionService)"""
raise NotImplementedError()
def SetDefaultConnectionService(self, NewDefaultConnectionService):
"""Returns (None)"""
raise NotImplementedError()

View file

@ -3,15 +3,18 @@ import json
from twisted.internet import defer from twisted.internet import defer
from txupnp.fault import UPnPError from txupnp.fault import UPnPError
from txupnp.soap import SOAPServiceManager from txupnp.soap import SOAPServiceManager
from txupnp.scpd import UPnPFallback
from txupnp.util import DeferredDict from txupnp.util import DeferredDict
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class UPnP(object): class UPnP(object):
def __init__(self, reactor): def __init__(self, reactor, miniupnpc_fallback=True):
self._reactor = reactor self._reactor = reactor
self._miniupnpc_fallback = miniupnpc_fallback
self.soap_manager = SOAPServiceManager(reactor) self.soap_manager = SOAPServiceManager(reactor)
self.miniupnpc_runner = None
@property @property
def lan_address(self): def lan_address(self):
@ -22,6 +25,8 @@ class UPnP(object):
try: try:
return self.soap_manager.get_runner() return self.soap_manager.get_runner()
except UPnPError as err: except UPnPError as err:
if self._miniupnpc_fallback and self.miniupnpc_runner:
return self.miniupnpc_runner
log.warning("upnp is not available: %s", err) log.warning("upnp is not available: %s", err)
def m_search(self, address, timeout=30, max_devices=2): def m_search(self, address, timeout=30, max_devices=2):
@ -44,11 +49,20 @@ class UPnP(object):
yield self.soap_manager.discover_services(timeout=timeout, max_devices=max_devices) yield self.soap_manager.discover_services(timeout=timeout, max_devices=max_devices)
found = True found = True
except defer.TimeoutError: except defer.TimeoutError:
log.warning("failed to find upnp gateway")
found = False found = False
finally: finally:
if not keep_listening: if not keep_listening:
self.soap_manager.sspd_factory.disconnect() self.soap_manager.sspd_factory.disconnect()
if not self.commands:
log.debug("trying miniupnpc fallback")
fallback = UPnPFallback()
success = yield fallback.discover()
if success:
log.info("successfully started miniupnpc fallback")
self.miniupnpc_runner = fallback
found = True
if not found:
log.warning("failed to find upnp gateway")
defer.returnValue(found) defer.returnValue(found)
def get_external_ip(self): def get_external_ip(self):