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
21 from twisted.internet.interfaces import IReactorMulticast
22 from twisted.python import log
24 from nattraverso.utils import is_rfc1918_ip, is_bogus_ip
26 @defer.inlineCallbacks
29 Returns a deferred which will be called with a
30 2-uple (lan_flag, ip_address) :
32 - True if it's a local network (RFC1918)
33 - False if it's a WAN address
35 - ip_address is the actual ip address
37 @return: A deferred called with the above defined tuple
38 @rtype: L{twisted.internet.defer.Deferred}
40 # first we try a connected udp socket, then via multicast
41 logging.debug("Resolving dns to get udp ip")
43 ipaddr = yield reactor.resolve('A.ROOT-SERVERS.NET')
47 udpprot = DatagramProtocol()
48 port = reactor.listenUDP(0, udpprot)
49 udpprot.transport.connect(ipaddr, 7)
50 localip = udpprot.transport.getHost().host
53 if is_bogus_ip(localip):
54 raise RuntimeError, "Invalid IP address returned"
56 defer.returnValue((is_rfc1918_ip(localip), localip))
58 logging.debug("Multicast ping to retrieve local IP")
59 ipaddr = yield _discover_multicast()
60 defer.returnValue((is_rfc1918_ip(ipaddr), ipaddr))
62 @defer.inlineCallbacks
63 def get_external_ip():
65 Returns a deferred which will be called with a
66 2-uple (wan_flag, ip_address):
68 - True if it's a WAN address
69 - False if it's a LAN address
70 - None if it's a localhost (127.0.0.1) address
71 - ip_address: the most accessible ip address of this machine
73 @return: A deferred called with the above defined tuple
74 @rtype: L{twisted.internet.defer.Deferred}
78 local, ipaddr = yield get_local_ip()
80 defer.returnValue((None, "127.0.0.1"))
82 defer.returnValue((True, ipaddr))
83 logging.debug("Got local ip, trying to use upnp to get WAN ip")
84 import nattraverso.pynupnp
86 ipaddr2 = yield nattraverso.pynupnp.get_external_ip()
88 defer.returnValue((False, ipaddr))
90 defer.returnValue((True, ipaddr2))
92 class _LocalNetworkMulticast(DatagramProtocol):
93 def __init__(self, nonce):
94 from p2pool.util import variable
97 self.address_received = variable.Event()
99 def datagramReceived(self, dgram, addr):
100 """Datagram received, we callback the IP address."""
101 logging.debug("Received multicast pong: %s; addr:%r", dgram, addr)
102 if dgram != self.nonce:
104 self.address_received.happened(addr[0])
106 @defer.inlineCallbacks
107 def _discover_multicast():
109 Local IP discovery protocol via multicast:
110 - Broadcast 3 ping multicast packet with "ping" in it
112 - Retrieve the ip address from the returning packet, which is ours
115 nonce = str(random.randrange(2**64))
116 p = _LocalNetworkMulticast(nonce)
118 for attempt in itertools.count():
119 port = 11000 + random.randint(0, 5000)
121 mcast = reactor.listenMulticast(port, p)
122 except CannotListenError:
130 yield mcast.joinGroup('239.255.255.250', socket.INADDR_ANY)
132 logging.debug("Sending multicast ping")
134 p.transport.write(nonce, ('239.255.255.250', port))
136 address, = yield p.address_received.get_deferred(5)
138 mcast.stopListening()
140 defer.returnValue(address)