]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/util/iputil.py
iputil/testutil: fix pyflakes errors/warnings
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / iputil.py
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
5
6 # from the Python Standard Library
7 import re, socket, sys
8
9 # from Twisted
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
15
16 def get_local_addresses_async(target='A.ROOT-SERVERS.NET'):
17     """
18     Return a Deferred that fires with a list of IPv4 addresses (as dotted-quad
19     strings) that are currently configured on this host.
20
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
24         reachable to.
25     """
26     if sys.platform == "cygwin":
27         return _cygwin_hack(target)
28
29     addresses = set()
30     addresses.add(get_local_ip_for(target))
31
32     d = _find_addresses_via_config()
33     def _collect(res):
34         addresses.update(res)
35         return addresses
36     d.addCallback(_collect)
37
38     return d
39
40 def get_local_ip_for(target):
41     """Find out what our IP address is for use by a given target.
42
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
45     """
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
52     return localip
53
54 # k: result of sys.platform, v: which kind of IP configuration reader we use
55 _platform_map = {
56     "linux-i386": "linux", # redhat
57     "linux-ppc": "linux",  # redhat
58     "linux2": "linux",     # debian
59     "win32": "win32",
60     "irix6-n32": "irix",
61     "irix6-n64": "irix",
62     "irix6": "irix",
63     "openbsd2": "bsd",
64     "darwin": "bsd",       # Mac OS X
65     "freebsd4": "bsd",
66     "freebsd5": "bsd",
67     "netbsd1": "bsd",
68     "sunos5": "sunos",
69     "cygwin": "cygwin",
70     }
71
72 class UnsupportedPlatformError(Exception):
73     pass
74
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)
82
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)
86
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)
91
92 # Irix 6.5
93 _irix_path = '/usr/etc/ifconfig'
94
95 # Solaris 2.x
96 _sunos_path = '/usr/sbin/ifconfig'
97
98 # k: platform string as provided in the value of _platform_map
99 # v: tuple of (path_to_tool, args, regex,)
100 _tool_map = {
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,),
107     }
108 def _find_addresses_via_config():
109     # originally by Greg Smith, hacked by Zooko to conform to Brian's API
110     
111     platform = _platform_map.get(sys.platform)
112     if not platform:
113         raise UnsupportedPlatformError(sys.platform)
114
115     (pathtotool, args, regex,) = _tool_map[platform]
116     
117     l = []
118     for executable in which(pathtotool):
119         l.append(_query(executable, args, regex))
120     dl = defer.DeferredList(l)
121     def _gather_results(res):
122         addresses = set()
123         for r in res:
124             if r[0]:
125                 addresses.update(r[1])
126         return addresses
127     dl.addCallback(_gather_results)
128     return dl
129
130 def _query(path, args, regex):
131     d = getProcessOutput(path, args)
132     def _parse(output):
133         addresses = set()
134         outputsplit = output.split('\n')
135         for outline in outputsplit:
136             m = regex.match(outline)
137             if m:
138                 d = m.groupdict()
139                 addresses.add(d['address'])
140     
141         return addresses
142     d.addCallback(_parse)
143     return d
144
145 def _cygwin_hack(target):
146     res = set()
147     for h in [target, "localhost", "127.0.0.1",]:
148         try:
149             res.add(get_local_ip_for(h))
150         except socket.gaierror:
151             pass
152
153     return defer.succeed(res)
154
155