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.python import log
12 from twisted.internet import reactor
13 from twisted.internet.protocol import DatagramProtocol
14 from twisted.internet.utils import getProcessOutput
15 from twisted.python.procutils import which
17 def get_local_addresses_async(target='A.ROOT-SERVERS.NET'):
19 Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
20 strings) that are currently configured on this host.
22 @param target: we want to learn an IP address they could try using to
23 connect to us; The default value is fine, but it might help if you
24 pass the address of a host that you are actually trying to be
27 if sys.platform == "cygwin":
28 return _cygwin_hack(target)
31 addresses.add(get_local_ip_for(target))
33 d = _find_addresses_via_config()
37 d.addCallback(_collect)
41 def get_local_ip_for(target):
42 """Find out what our IP address is for use by a given target.
44 @returns: the IP address as a dotted-quad string which could be used by
45 'target' to connect to us. It might work for them, it might not
47 target_ipaddr = socket.gethostbyname(target)
48 udpprot = DatagramProtocol()
49 port = reactor.listenUDP(0, udpprot)
50 udpprot.transport.connect(target_ipaddr, 7)
51 localip = udpprot.transport.getHost().host
52 port.stopListening() # note, this returns a Deferred
55 # k: result of sys.platform, v: which kind of IP configuration reader we use
57 "linux-i386": "linux", # redhat
58 "linux-ppc": "linux", # redhat
59 "linux2": "linux", # debian
65 "darwin": "bsd", # Mac OS X
73 class UnsupportedPlatformError(Exception):
76 # Wow, I'm really amazed at home much mileage we've gotten out of calling
77 # the external route.exe program on windows... It appears to work on all
78 # versions so far. Still, the real system calls would much be preferred...
79 # ... thus wrote Greg Smith in time immemorial...
80 _win32_path = 'route.exe'
81 _win32_args = ('print',)
82 _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)
84 # These work in Redhat 6.x and Debian 2.2 potato
85 _linux_path = '/sbin/ifconfig'
86 _linux_re = re.compile('^\s*inet addr:(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
88 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
89 _netbsd_path = '/sbin/ifconfig'
90 _netbsd_args = ('-a',)
91 _netbsd_re = re.compile('^\s+inet (?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
94 _irix_path = '/usr/etc/ifconfig'
97 _sunos_path = '/usr/sbin/ifconfig'
99 # k: platform string as provided in the value of _platform_map
100 # v: tuple of (path_to_tool, args, regex,)
102 "linux": (_linux_path, (), _linux_re,),
103 "win32": (_win32_path, _win32_args, _win32_re,),
104 "cygwin": (_win32_path, _win32_args, _win32_re,),
105 "bsd": (_netbsd_path, _netbsd_args, _netbsd_re,),
106 "irix": (_irix_path, _netbsd_args, _netbsd_re,),
107 "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
109 def _find_addresses_via_config():
110 # originally by Greg Smith, hacked by Zooko to conform to Brian's API
112 platform = _platform_map.get(sys.platform)
114 raise UnsupportedPlatformError(sys.platform)
116 (pathtotool, args, regex,) = _tool_map[platform]
119 for executable in which(pathtotool):
120 l.append(_query(executable, args, regex))
121 dl = defer.DeferredList(l)
122 def _gather_results(res):
126 addresses.update(r[1])
128 dl.addCallback(_gather_results)
131 def _query(path, args, regex):
132 d = getProcessOutput(path, args)
135 outputsplit = output.split('\n')
136 for outline in outputsplit:
137 m = regex.match(outline)
140 addresses.add(d['address'])
143 d.addCallback(_parse)
146 def _cygwin_hack(target):
148 for h in [target, "localhost", "127.0.0.1",]:
150 res.add(get_local_ip_for(h))
151 except socket.gaierror:
154 return defer.succeed(res)