2 Generic methods to retreive the IP address of the local machine.
6 @author: Raphael Slinckx
7 @copyright: Copyright 2005
9 @contact: U{raphael@slinckx.net<mailto:raphael@slinckx.net>}
15 import random, socket, logging, itertools
17 from twisted.internet import defer, reactor
19 from twisted.internet.protocol import DatagramProtocol
20 from twisted.internet.error import CannotListenError
22 from nattraverso.utils import is_rfc1918_ip, is_bogus_ip
24 @defer.inlineCallbacks
27 Returns a deferred which will be called with a
28 2-uple (lan_flag, ip_address) :
30 - True if it's a local network (RFC1918)
31 - False if it's a WAN address
33 - ip_address is the actual ip address
35 @return: A deferred called with the above defined tuple
36 @rtype: L{twisted.internet.defer.Deferred}
38 # first we try a connected udp socket, then via multicast
39 logging.debug("Resolving dns to get udp ip")
41 ipaddr = yield reactor.resolve('A.ROOT-SERVERS.NET')
45 udpprot = DatagramProtocol()
46 port = reactor.listenUDP(0, udpprot)
47 udpprot.transport.connect(ipaddr, 7)
48 localip = udpprot.transport.getHost().host
51 if is_bogus_ip(localip):
52 raise RuntimeError, "Invalid IP address returned"
54 defer.returnValue((is_rfc1918_ip(localip), localip))
56 logging.debug("Multicast ping to retrieve local IP")
57 ipaddr = yield _discover_multicast()
58 defer.returnValue((is_rfc1918_ip(ipaddr), ipaddr))
60 @defer.inlineCallbacks
61 def get_external_ip():
63 Returns a deferred which will be called with a
64 2-uple (wan_flag, ip_address):
66 - True if it's a WAN address
67 - False if it's a LAN address
68 - None if it's a localhost (127.0.0.1) address
69 - ip_address: the most accessible ip address of this machine
71 @return: A deferred called with the above defined tuple
72 @rtype: L{twisted.internet.defer.Deferred}
76 local, ipaddr = yield get_local_ip()
78 defer.returnValue((None, "127.0.0.1"))
80 defer.returnValue((True, ipaddr))
81 logging.debug("Got local ip, trying to use upnp to get WAN ip")
82 import nattraverso.pynupnp
84 ipaddr2 = yield nattraverso.pynupnp.get_external_ip()
86 defer.returnValue((False, ipaddr))
88 defer.returnValue((True, ipaddr2))
90 class _LocalNetworkMulticast(DatagramProtocol):
91 def __init__(self, nonce):
92 from p2pool.util import variable
95 self.address_received = variable.Event()
97 def datagramReceived(self, dgram, addr):
98 """Datagram received, we callback the IP address."""
99 logging.debug("Received multicast pong: %s; addr:%r", dgram, addr)
100 if dgram != self.nonce:
102 self.address_received.happened(addr[0])
104 @defer.inlineCallbacks
105 def _discover_multicast():
107 Local IP discovery protocol via multicast:
108 - Broadcast 3 ping multicast packet with "ping" in it
110 - Retrieve the ip address from the returning packet, which is ours
113 nonce = str(random.randrange(2**64))
114 p = _LocalNetworkMulticast(nonce)
116 for attempt in itertools.count():
117 port = 11000 + random.randint(0, 5000)
119 mcast = reactor.listenMulticast(port, p)
120 except CannotListenError:
128 yield mcast.joinGroup('239.255.255.250', socket.INADDR_ANY)
130 logging.debug("Sending multicast ping")
132 p.transport.write(nonce, ('239.255.255.250', port))
134 address, = yield p.address_received.get_deferred(5)
136 mcast.stopListening()
138 defer.returnValue(address)