UPnP fixes

- UPnP handles errors better
- it tries to bind external interface (previously sometimes it searched
on 127.0.0.1 resulting in no routers being detected)
This commit is contained in:
mailchuck 2016-03-21 21:52:10 +01:00 committed by Peter Surda
parent e03803e4e8
commit a1b44aab44

View File

@ -94,24 +94,10 @@ class Router:
service_types = dom.getElementsByTagName('serviceType') service_types = dom.getElementsByTagName('serviceType')
for service in service_types: for service in service_types:
if service.childNodes[0].data.find('WANIPConnection') > 0: if service.childNodes[0].data.find('WANIPConnection') > 0 or \
service.childNodes[0].data.find('WANPPPConnection') > 0:
self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
# get local IP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
logger.debug("Connecting to %s:%i", self.address, self.routerPath.port)
s.connect ((self.address, self.routerPath.port))
except:
pass
self.localAddress = s.getsockname()[0]
logger.debug("Local IP: %s", self.localAddress)
try:
s.shutdown(socket.SHUT_RDWR)
s.close()
except:
pass
def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1): def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1):
from debug import logger from debug import logger
resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [ resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [
@ -155,15 +141,27 @@ class Router:
'Content-Type': 'text/xml' 'Content-Type': 'text/xml'
} }
) )
resp = conn.getresponse().read() resp = conn.getresponse()
dom = parseString(resp) conn.close()
if resp.status == 500:
respData = resp.read()
try:
dom = parseString(respData)
errinfo = dom.getElementsByTagName('errorDescription') errinfo = dom.getElementsByTagName('errorDescription')
if len(errinfo) > 0: if len(errinfo) > 0:
logger.error("UPnP error: %s", resp) logger.error("UPnP error: %s", respData)
raise UPnPError(errinfo[0].childNodes[0].data) raise UPnPError(errinfo[0].childNodes[0].data)
except:
raise UPnPError("Unable to parse SOAP error: %s", respData)
return resp return resp
class uPnPThread(threading.Thread, StoppableThread): class uPnPThread(threading.Thread, StoppableThread):
SSDP_ADDR = "239.255.255.250"
GOOGLE_DNS = "8.8.8.8"
SSDP_PORT = 1900
SSDP_MX = 2
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
def __init__ (self): def __init__ (self):
threading.Thread.__init__(self, name="uPnPThread") threading.Thread.__init__(self, name="uPnPThread")
self.localPort = shared.config.getint('bitmessagesettings', 'port') self.localPort = shared.config.getint('bitmessagesettings', 'port')
@ -171,10 +169,12 @@ class uPnPThread(threading.Thread, StoppableThread):
self.extPort = shared.config.getint('bitmessagesettings', 'extport') self.extPort = shared.config.getint('bitmessagesettings', 'extport')
except: except:
self.extPort = None self.extPort = None
self.localIP = self.getLocalIP()
self.routers = [] self.routers = []
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind((self.localIP, 10000))
self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
self.sock.settimeout(2) self.sock.settimeout(5)
self.sendSleep = 60 self.sendSleep = 60
self.initStop() self.initStop()
@ -182,6 +182,7 @@ class uPnPThread(threading.Thread, StoppableThread):
from debug import logger from debug import logger
logger.debug("Starting UPnP thread") logger.debug("Starting UPnP thread")
logger.debug("Local IP: %s", self.localIP)
lastSent = 0 lastSent = 0
while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'): while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'):
if time.time() - lastSent > self.sendSleep and len(self.routers) == 0: if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
@ -227,21 +228,23 @@ class uPnPThread(threading.Thread, StoppableThread):
shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed'))) shared.UISignalQueue.put(('updateStatusBar', tr.translateText("MainWindow",'UPnP port mapping removed')))
logger.debug("UPnP thread done") logger.debug("UPnP thread done")
def getLocalIP(self):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.connect((uPnPThread.GOOGLE_DNS, 1))
return s.getsockname()[0]
def sendSearchRouter(self): def sendSearchRouter(self):
from debug import logger from debug import logger
SSDP_ADDR = "239.255.255.250"
SSDP_PORT = 1900
SSDP_MX = 2
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \ ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
"HOST: %s:%d\r\n" % (SSDP_ADDR, SSDP_PORT) + \ "HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
"MAN: \"ssdp:discover\"\r\n" + \ "MAN: \"ssdp:discover\"\r\n" + \
"MX: %d\r\n" % (SSDP_MX, ) + \ "MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
"ST: %s\r\n" % (SSDP_ST, ) + "\r\n" "ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
try: try:
logger.debug("Sending UPnP query") logger.debug("Sending UPnP query")
self.sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) self.sock.sendto(ssdpRequest, (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT))
except: except:
logger.exception("UPnP send query failed") logger.exception("UPnP send query failed")
@ -251,7 +254,7 @@ class uPnPThread(threading.Thread, StoppableThread):
for i in range(50): for i in range(50):
try: try:
routerIP, = unpack('>I', socket.inet_aton(router.address)) routerIP, = unpack('>I', socket.inet_aton(router.address))
localIP = router.localAddress localIP = self.localIP
if i == 0: if i == 0:
extPort = self.localPort # try same port first extPort = self.localPort # try same port first
elif i == 1 and self.extPort: elif i == 1 and self.extPort: