fixed resource leak and incorrect exception handling in nattraverso upnp library
authorForrest Voight <forrest@forre.st>
Mon, 8 Aug 2011 20:22:08 +0000 (16:22 -0400)
committerForrest Voight <forrest@forre.st>
Mon, 8 Aug 2011 20:22:08 +0000 (16:22 -0400)
nattraverso/ipdiscover.py
p2pool/main.py

index f8b5012..d18ec07 100644 (file)
@@ -123,8 +123,8 @@ def _get_via_multicast():
        except:
                raise
 
-       logging.debug("Multicast ping to retreive local IP")
-       return LocalNetworkMulticast().discover().addCallback(_got_multicast_ip)
+       logging.debug("Multicast ping to retrieve local IP")
+       return _discover_multicast().addCallback(_got_multicast_ip)
 
 def _get_via_connected_udp(ipaddr):
        """
@@ -142,66 +142,62 @@ def _get_via_connected_udp(ipaddr):
        port.stopListening()
        
        if is_bogus_ip(localip):
-               raise RuntimeError, "Invalid IP addres returned"
+               raise RuntimeError, "Invalid IP address returned"
        else:
                return (is_rfc1918_ip(localip), localip)
                
-class LocalNetworkMulticast(DatagramProtocol, object):
-       """
-       Local IP discovery protocol via multicast:
-               - Broadcast 3 ping multicast packet with "ping" in it
-               - Wait for an answer
-               - Retreive the ip address from the returning packet, which is ours
-       """
-       def __init__(self, *args, **kwargs):
-               super(LocalNetworkMulticast, self).__init__(*args, **kwargs)
-               
-               # Nothing found yet
-               self.completed = False
-               self.result = defer.Deferred()
+class _LocalNetworkMulticast(DatagramProtocol):
+       def __init__(self, nonce):
+               from p2pool.util import variable
                
-       def discover(self):
-               """
-               Launch the discovery of an UPnP device on the local network. You should
-               always call this after having created the object.
-               
-               >>> result = LocalNetworkMulticast().discover().addCallback(got_upnpdevice_ip)
-               
-               @return: A deferred called with the IP address of the UPnP device
-               @rtype: L{twisted.internet.defer.Deferred}
-               """
-               #The port we listen on
-               mcast_port = 0
-               #The result of listenMulticast
-               mcast = None
-
-               # 5 different UDP ports
-               ports = [11000+random.randint(0, 5000) for port in range(5)]
-               for attempt, port in enumerate(ports):
-                       try:
-                               mcast = reactor.listenMulticast(port, self)
-                               mcast_port = port
-                               break
-                       except CannotListenError:
-                               if attempt < 5:
-                                       print "Trying another multicast UDP port", port
-                               else:
-                                       raise
-               
-               logging.debug("Sending multicast ping")
-               mcast.joinGroup('239.255.255.250', socket.INADDR_ANY)
-               self.transport.write('ping', ('239.255.255.250', mcast_port))
-               self.transport.write('ping', ('239.255.255.250', mcast_port))
-               self.transport.write('ping', ('239.255.255.250', mcast_port))
-               return self.result
-
+               self.nonce = nonce
+               self.address_received = variable.Event()
+       
        def datagramReceived(self, dgram, addr):
                """Datagram received, we callback the IP address."""
                logging.debug("Received multicast pong: %s; addr:%r", dgram, addr)
-               if self.completed or dgram != 'ping':
-                       # Result already handled, do nothing
+               if dgram != self.nonce:
                        return
+               self.address_received.happened(addr[0])
+
+@defer.inlineCallbacks
+def _discover_multicast():
+       """
+       Local IP discovery protocol via multicast:
+               - Broadcast 3 ping multicast packet with "ping" in it
+               - Wait for an answer
+               - Retrieve the ip address from the returning packet, which is ours
+       """
+       
+       nonce = str(random.randrange(2**64))
+       p = _LocalNetworkMulticast(nonce)
+       
+       # 5 different UDP ports
+       ports = [11000+random.randint(0, 5000) for port in range(5)]
+       for attempt, port in enumerate(ports):
+               try:
+                       mcast = reactor.listenMulticast(port, p)
+                       mcast_port = port
+               except CannotListenError:
+                       if attempt < 5:
+                               print "Trying another multicast UDP port", port
+                       else:
+                               raise
                else:
-                       self.completed = True
-                       # Return the upn device ip address
-                       self.result.callback(addr[0])
+                       break
+       
+       try:
+               yield mcast.joinGroup('239.255.255.250', socket.INADDR_ANY)
+               
+               try:
+                       logging.debug("Sending multicast ping")
+                       for i in xrange(3):
+                               p.transport.write(nonce, ('239.255.255.250', mcast_port))
+                       
+                       address, = yield p.address_received.get_deferred(5)
+               finally:
+                       mcast.leaveGroup('239.255.255.250', socket.INADDR_ANY)
+       finally:
+               mcast.stopListening()
+       
+       defer.returnValue(address)
index 50adf8a..da771f3 100644 (file)
@@ -301,7 +301,7 @@ def main(args):
                     yield pm._upnp.add_port_mapping(lan_ip, args.net.P2P_PORT, args.net.P2P_PORT, 'p2pool', 'TCP')
                 except:
                     if p2pool_init.DEBUG:
-                        log.err()
+                        log.err(None, "UPnP error:")
                 yield deferral.sleep(random.expovariate(1/120))
         
         if args.upnp: