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, sorted in descending
20 order of how likely we think they are to work.
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
28 addresses.append(get_local_ip_for(target))
30 if sys.platform == "cygwin":
31 d = _cygwin_hack_find_addresses(target)
33 d = _find_addresses_via_config()
37 if not addr in addresses:
38 addresses.append(addr)
40 d.addCallback(_collect)
44 def get_local_ip_for(target):
45 """Find out what our IP address is for use by a given target.
47 @returns: the IP address as a dotted-quad string which could be used by
48 'target' to connect to us. It might work for them, it might not
50 target_ipaddr = socket.gethostbyname(target)
51 udpprot = DatagramProtocol()
52 port = reactor.listenUDP(0, udpprot)
53 udpprot.transport.connect(target_ipaddr, 7)
54 localip = udpprot.transport.getHost().host
55 port.stopListening() # note, this returns a Deferred
58 # k: result of sys.platform, v: which kind of IP configuration reader we use
60 "linux-i386": "linux", # redhat
61 "linux-ppc": "linux", # redhat
62 "linux2": "linux", # debian
68 "darwin": "bsd", # Mac OS X
76 class UnsupportedPlatformError(Exception):
79 # Wow, I'm really amazed at home much mileage we've gotten out of calling
80 # the external route.exe program on windows... It appears to work on all
81 # versions so far. Still, the real system calls would much be preferred...
82 # ... thus wrote Greg Smith in time immemorial...
83 _win32_path = 'route.exe'
84 _win32_args = ('print',)
85 _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)
87 # These work in Redhat 6.x and Debian 2.2 potato
88 _linux_path = '/sbin/ifconfig'
89 _linux_re = re.compile('^\s*inet addr:(?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
91 # NetBSD 1.4 (submitted by Rhialto), Darwin, Mac OS X
92 _netbsd_path = '/sbin/ifconfig'
93 _netbsd_args = ('-a',)
94 _netbsd_re = re.compile('^\s+inet (?P<address>\d+\.\d+\.\d+\.\d+)\s.+$', flags=re.M|re.I|re.S)
97 _irix_path = '/usr/etc/ifconfig'
100 _sunos_path = '/usr/sbin/ifconfig'
102 # k: platform string as provided in the value of _platform_map
103 # v: tuple of (path_to_tool, args, regex,)
105 "linux": (_linux_path, (), _linux_re,),
106 "win32": (_win32_path, _win32_args, _win32_re,),
107 "cygwin": (_win32_path, _win32_args, _win32_re,),
108 "bsd": (_netbsd_path, _netbsd_args, _netbsd_re,),
109 "irix": (_irix_path, _netbsd_args, _netbsd_re,),
110 "sunos": (_sunos_path, _netbsd_args, _netbsd_re,),
112 def _find_addresses_via_config():
113 # originally by Greg Smith, hacked by Zooko to conform to Brian's API
115 platform = _platform_map.get(sys.platform)
117 raise UnsupportedPlatformError(sys.platform)
119 (pathtotool, args, regex,) = _tool_map[platform]
122 for executable in which(pathtotool):
123 l.append(_query(executable, args, regex))
124 dl = defer.DeferredList(l)
125 def _gather_results(res):
127 for (succ, addrs,) in res:
130 if addr not in addresses:
131 addresses.append(addr)
133 dl.addCallback(_gather_results)
136 def _query(path, args, regex):
137 d = getProcessOutput(path, args)
140 outputsplit = output.split('\n')
141 for outline in outputsplit:
142 m = regex.match(outline)
144 addr = m.groupdict()['address']
145 if addr not in addresses:
146 addresses.append(addr)
149 d.addCallback(_parse)
152 def _cygwin_hack_find_addresses(target):
154 for h in [target, "localhost", "127.0.0.1",]:
156 addr = get_local_ip_for(h)
157 if addr not in addresses:
158 addresses.append(addr)
159 except socket.gaierror:
162 return defer.succeed(addresses)