From a1b44aab4456f95d65685fd16e466ba939cf9d57 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Mon, 21 Mar 2016 21:52:10 +0100 Subject: [PATCH] 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) --- src/upnp.py | 67 ++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/upnp.py b/src/upnp.py index 4c54ef86..5f952c73 100644 --- a/src/upnp.py +++ b/src/upnp.py @@ -94,24 +94,10 @@ class Router: service_types = dom.getElementsByTagName('serviceType') 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 - # 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): from debug import logger resp = self.soapRequest('WANIPConnection:1', 'AddPortMapping', [ @@ -155,15 +141,27 @@ class Router: 'Content-Type': 'text/xml' } ) - resp = conn.getresponse().read() - dom = parseString(resp) - errinfo = dom.getElementsByTagName('errorDescription') - if len(errinfo) > 0: - logger.error("UPnP error: %s", resp) - raise UPnPError(errinfo[0].childNodes[0].data) + resp = conn.getresponse() + conn.close() + if resp.status == 500: + respData = resp.read() + try: + dom = parseString(respData) + errinfo = dom.getElementsByTagName('errorDescription') + if len(errinfo) > 0: + logger.error("UPnP error: %s", respData) + raise UPnPError(errinfo[0].childNodes[0].data) + except: + raise UPnPError("Unable to parse SOAP error: %s", respData) return resp 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): threading.Thread.__init__(self, name="uPnPThread") self.localPort = shared.config.getint('bitmessagesettings', 'port') @@ -171,10 +169,12 @@ class uPnPThread(threading.Thread, StoppableThread): self.extPort = shared.config.getint('bitmessagesettings', 'extport') except: self.extPort = None + self.localIP = self.getLocalIP() self.routers = [] 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.settimeout(2) + self.sock.settimeout(5) self.sendSleep = 60 self.initStop() @@ -182,6 +182,7 @@ class uPnPThread(threading.Thread, StoppableThread): from debug import logger logger.debug("Starting UPnP thread") + logger.debug("Local IP: %s", self.localIP) lastSent = 0 while shared.shutdown == 0 and shared.safeConfigGetBoolean('bitmessagesettings', 'upnp'): 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'))) 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): 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" + \ - "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" + \ - "MX: %d\r\n" % (SSDP_MX, ) + \ - "ST: %s\r\n" % (SSDP_ST, ) + "\r\n" + "MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \ + "ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n" try: logger.debug("Sending UPnP query") - self.sock.sendto(ssdpRequest, (SSDP_ADDR, SSDP_PORT)) + self.sock.sendto(ssdpRequest, (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT)) except: logger.exception("UPnP send query failed") @@ -251,7 +254,7 @@ class uPnPThread(threading.Thread, StoppableThread): for i in range(50): try: routerIP, = unpack('>I', socket.inet_aton(router.address)) - localIP = router.localAddress + localIP = self.localIP if i == 0: extPort = self.localPort # try same port first elif i == 1 and self.extPort: