1 # portions extracted from ipaddresslib by Autonomous Zone Industries, LGPL (author: Greg Smith)
2 # portions adapted from nattraverso.ipdiscover
3 # portions authored by Brian Warner, working for Allmydata
4 # most recent version authored by Zooko O'Whielacronx, working for Allmydata
6 # from the Python Standard Library
10 from twisted.internet import defer
11 from twisted.internet import reactor
12 from twisted.internet.protocol import DatagramProtocol
13 from twisted.internet.utils import getProcessOutput
14 from twisted.python.procutils import which
16 def get_local_addresses_async(target='A.ROOT-SERVERS.NET'):
18 Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
19 strings) that are currently configured on this host.
21 @param target: we want to learn an IP address they could try using to
22 connect to us; The default value is fine, but it might help if you
23 pass the address of a host that you are actually trying to be
26 if sys.platform == "cygwin":
27 return _cygwin_hack(target)
30 addresses.add(get_local_ip_for(target))
32 d = _find_addresses_via_config()
36 d.addCallback(_collect)
40 def get_local_ip_for(target):
41 """Find out what our IP address is for use by a given target.
43 @returns: the IP address as a dotted-quad string which could be used by
44 'target' to connect to us. It might work for them, it might not
46 target_ipaddr = socket.gethostbyname(target)
47 udpprot = DatagramProtocol()
48 port = reactor.listenUDP(0, udpprot)
49 udpprot.transport.connect(target_ipaddr, 7)
50 localip = udpprot.transport.getHost().host
51 port.stopListening() # note, this returns a Deferred
54 # k: result of sys.platform, v: which kind of IP configuration reader we use
56 "linux-i386": "linux", # redhat
57 "linux-ppc": "linux", # redhat
58 "linux2": "linux", # debian
64 "darwin": "bsd", # Mac OS X
72 class UnsupportedPlatformError(Exception):
75 # Wow, I'm really amazed at home much mileage we've gotten out of calling
76 # the external route.exe program on windows... It appears to work on all
77 # versions so far. Still, the real system calls would much be preferred...
78 # ... thus wrote Greg Smith in time immemorial...
79 _win32_path = 'route.exe'
80 _win32_args = ('print',)
81 _win32_re = re.compile('^\s*\d+\.\d+\.\d+\.\d+\s.+\s(?P<address>\d+\.\d+\.\d+\.\d+)\s+(?P<metric>\d+)\s*$', flags=re.M|re.I|re.S)
83 # These work in Redhat 6.x and Debian 2.2 potato
84 _linux_path = '/sbin/ifconfig'
85 _linux_re = re.compile('^\s*inet addr:(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
87 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
88 _netbsd_path = '/sbin/ifconfig'
89 _netbsd_args = ('-a',)
90 _netbsd_re = re.compile('^\s+inet (?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
93 _irix_path = '/usr/etc/ifconfig'
96 _sunos_path = '/usr/sbin/ifconfig'
98 # k: platform string as provided in the value of _platform_map
99 # v: tuple of (path_to_tool, args, regex,)
101 "linux": (_linux_path, (), _linux_re,),
102 "win32": (_win32_path, _win32_args, _win32_re,),
103 "cygwin": (_win32_path, _win32_args, _win32_re,),
104 "bsd": (_netbsd_path, _netbsd_args, _netbsd_re,),
105 "irix": (_irix_path, _netbsd_args, _netbsd_re,),
106 "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
108 def _find_addresses_via_config():
109 # originally by Greg Smith, hacked by Zooko to conform to Brian's API
111 platform = _platform_map.get(sys.platform)
113 raise UnsupportedPlatformError(sys.platform)
115 (pathtotool, args, regex,) = _tool_map[platform]
118 for executable in which(pathtotool):
119 l.append(_query(executable, args, regex))
120 dl = defer.DeferredList(l)
121 def _gather_results(res):
125 addresses.update(r[1])
127 dl.addCallback(_gather_results)
130 def _query(path, args, regex):
131 d = getProcessOutput(path, args)
134 outputsplit = output.split('\n')
135 for outline in outputsplit:
136 m = regex.match(outline)
139 addresses.add(d['address'])
142 d.addCallback(_parse)
145 def _cygwin_hack(target):
147 for h in [target, "localhost", "127.0.0.1",]:
149 res.add(get_local_ip_for(h))
150 except socket.gaierror:
153 return defer.succeed(res)