##// END OF EJS Templates
relax ipconfig matching on Windows...
MinRK -
Show More
@@ -1,273 +1,275 b''
1 1 """Simple utility for building a list of local IPs using the socket module.
2 2 This module defines two constants:
3 3
4 4 LOCALHOST : The loopback interface, or the first interface that points to this
5 5 machine. It will *almost* always be '127.0.0.1'
6 6
7 7 LOCAL_IPS : A list of IP addresses, loopback first, that point to this machine.
8 8 This will include LOCALHOST, PUBLIC_IPS, and aliases for all hosts,
9 9 such as '0.0.0.0'.
10 10
11 11 PUBLIC_IPS : A list of public IP addresses that point to this machine.
12 12 Use these to tell remote clients where to find you.
13 13 """
14 14 #-----------------------------------------------------------------------------
15 15 # Copyright (C) 2010 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-----------------------------------------------------------------------------
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Imports
23 23 #-----------------------------------------------------------------------------
24 24
25 25 import os
26 import re
26 27 import socket
27 28
28 29 from .data import uniq_stable
29 30 from .process import get_output_error_code
30 31 from .warn import warn
31 32
32 33 #-----------------------------------------------------------------------------
33 34 # Code
34 35 #-----------------------------------------------------------------------------
35 36
36 37 LOCAL_IPS = []
37 38 PUBLIC_IPS = []
38 39
39 40 LOCALHOST = ''
40 41
41 42 def _only_once(f):
42 43 """decorator to only run a function once"""
43 44 f.called = False
44 45 def wrapped(**kwargs):
45 46 if f.called:
46 47 return
47 48 ret = f(**kwargs)
48 49 f.called = True
49 50 return ret
50 51 return wrapped
51 52
52 53 def _requires_ips(f):
53 54 """decorator to ensure load_ips has been run before f"""
54 55 def ips_loaded(*args, **kwargs):
55 56 _load_ips()
56 57 return f(*args, **kwargs)
57 58 return ips_loaded
58 59
59 60 # subprocess-parsing ip finders
60 61 class NoIPAddresses(Exception):
61 62 pass
62 63
63 64 def _populate_from_list(addrs):
64 65 """populate local and public IPs from flat list of all IPs"""
65 66 if not addrs:
66 67 raise NoIPAddresses
67 68
68 69 global LOCALHOST
69 70 public_ips = []
70 71 local_ips = []
71 72
72 73 for ip in addrs:
73 74 local_ips.append(ip)
74 75 if not ip.startswith('127.'):
75 76 public_ips.append(ip)
76 77 elif not LOCALHOST:
77 78 LOCALHOST = ip
78 79
79 80 if not LOCALHOST:
80 81 LOCALHOST = '127.0.0.1'
81 82 local_ips.insert(0, LOCALHOST)
82 83
83 84 local_ips.extend(['0.0.0.0', ''])
84 85
85 86 LOCAL_IPS[:] = uniq_stable(local_ips)
86 87 PUBLIC_IPS[:] = uniq_stable(public_ips)
87 88
88 89 def _load_ips_ifconfig():
89 90 """load ip addresses from `ifconfig` output (posix)"""
90 91
91 92 out, err, rc = get_output_error_code('ifconfig')
92 93 if rc:
93 94 # no ifconfig, it's usually in /sbin and /sbin is not on everyone's PATH
94 95 out, err, rc = get_output_error_code('/sbin/ifconfig')
95 96 if rc:
96 97 raise IOError("no ifconfig: %s" % err)
97 98
98 99 lines = out.splitlines()
99 100 addrs = []
100 101 for line in lines:
101 102 blocks = line.lower().split()
102 103 if (len(blocks) >= 2) and (blocks[0] == 'inet'):
103 104 addrs.append(blocks[1])
104 105 _populate_from_list(addrs)
105 106
106 107
107 108 def _load_ips_ip():
108 109 """load ip addresses from `ip addr` output (Linux)"""
109 110 out, err, rc = get_output_error_code('ip addr')
110 111 if rc:
111 112 raise IOError("no ip: %s" % err)
112 113
113 114 lines = out.splitlines()
114 115 addrs = []
115 116 for line in lines:
116 117 blocks = line.lower().split()
117 118 if (len(blocks) >= 2) and (blocks[0] == 'inet'):
118 119 addrs.append(blocks[1].split('/')[0])
119 120 _populate_from_list(addrs)
120 121
122 _ipconfig_ipv4_pat = re.compile(r'ipv4.*(\d+\.\d+\.\d+\.\d+)$', re.IGNORECASE)
121 123
122 124 def _load_ips_ipconfig():
123 125 """load ip addresses from `ipconfig` output (Windows)"""
124 126 out, err, rc = get_output_error_code('ipconfig')
125 127 if rc:
126 128 raise IOError("no ipconfig: %s" % err)
127 129
128 130 lines = out.splitlines()
129 131 addrs = ['127.0.0.1']
130 132 for line in lines:
131 line = line.lower().split()
132 if line[:2] == ['ipv4', 'address']:
133 addrs.append(line.split()[-1])
133 m = _ipconfig_ipv4_pat.match(line.strip())
134 if m:
135 addrs.append(m.group(1))
134 136 _populate_from_list(addrs)
135 137
136 138
137 139 def _load_ips_netifaces():
138 140 """load ip addresses with netifaces"""
139 141 import netifaces
140 142 global LOCALHOST
141 143 local_ips = []
142 144 public_ips = []
143 145
144 146 # list of iface names, 'lo0', 'eth0', etc.
145 147 for iface in netifaces.interfaces():
146 148 # list of ipv4 addrinfo dicts
147 149 ipv4s = netifaces.ifaddresses(iface).get(netifaces.AF_INET, [])
148 150 for entry in ipv4s:
149 151 addr = entry.get('addr')
150 152 if not addr:
151 153 continue
152 154 if not (iface.startswith('lo') or addr.startswith('127.')):
153 155 public_ips.append(addr)
154 156 elif not LOCALHOST:
155 157 LOCALHOST = addr
156 158 local_ips.append(addr)
157 159 if not LOCALHOST:
158 160 # we never found a loopback interface (can this ever happen?), assume common default
159 161 LOCALHOST = '127.0.0.1'
160 162 local_ips.insert(0, LOCALHOST)
161 163 local_ips.extend(['0.0.0.0', ''])
162 164 LOCAL_IPS[:] = uniq_stable(local_ips)
163 165 PUBLIC_IPS[:] = uniq_stable(public_ips)
164 166
165 167
166 168 def _load_ips_gethostbyname():
167 169 """load ip addresses with socket.gethostbyname_ex
168 170
169 171 This can be slow.
170 172 """
171 173 global LOCALHOST
172 174 try:
173 175 LOCAL_IPS[:] = socket.gethostbyname_ex('localhost')[2]
174 176 except socket.error:
175 177 # assume common default
176 178 LOCAL_IPS[:] = ['127.0.0.1']
177 179
178 180 try:
179 181 hostname = socket.gethostname()
180 182 PUBLIC_IPS[:] = socket.gethostbyname_ex(hostname)[2]
181 183 # try hostname.local, in case hostname has been short-circuited to loopback
182 184 if not hostname.endswith('.local') and all(ip.startswith('127') for ip in PUBLIC_IPS):
183 185 PUBLIC_IPS[:] = socket.gethostbyname_ex(socket.gethostname() + '.local')[2]
184 186 except socket.error:
185 187 pass
186 188 finally:
187 189 PUBLIC_IPS[:] = uniq_stable(PUBLIC_IPS)
188 190 LOCAL_IPS.extend(PUBLIC_IPS)
189 191
190 192 # include all-interface aliases: 0.0.0.0 and ''
191 193 LOCAL_IPS.extend(['0.0.0.0', ''])
192 194
193 195 LOCAL_IPS[:] = uniq_stable(LOCAL_IPS)
194 196
195 197 LOCALHOST = LOCAL_IPS[0]
196 198
197 199 def _load_ips_dumb():
198 200 """Fallback in case of unexpected failure"""
199 201 global LOCALHOST
200 202 LOCALHOST = '127.0.0.1'
201 203 LOCAL_IPS[:] = [LOCALHOST, '0.0.0.0', '']
202 204 PUBLIC_IPS[:] = []
203 205
204 206 @_only_once
205 207 def _load_ips(suppress_exceptions=True):
206 208 """load the IPs that point to this machine
207 209
208 210 This function will only ever be called once.
209 211
210 212 It will use netifaces to do it quickly if available.
211 213 Then it will fallback on parsing the output of ifconfig / ip addr / ipconfig, as appropriate.
212 214 Finally, it will fallback on socket.gethostbyname_ex, which can be slow.
213 215 """
214 216
215 217 try:
216 218 # first priority, use netifaces
217 219 try:
218 220 return _load_ips_netifaces()
219 221 except ImportError:
220 222 pass
221 223
222 224 # second priority, parse subprocess output (how reliable is this?)
223 225
224 226 if os.name == 'nt':
225 227 try:
226 228 return _load_ips_ipconfig()
227 229 except (IOError, NoIPAddresses):
228 230 pass
229 231 else:
230 232 try:
231 233 return _load_ips_ifconfig()
232 234 except (IOError, NoIPAddresses):
233 235 pass
234 236 try:
235 237 return _load_ips_ip()
236 238 except (IOError, NoIPAddresses):
237 239 pass
238 240
239 241 # lowest priority, use gethostbyname
240 242
241 243 return _load_ips_gethostbyname()
242 244 except Exception as e:
243 245 if not suppress_exceptions:
244 246 raise
245 247 # unexpected error shouldn't crash, load dumb default values instead.
246 248 warn("Unexpected error discovering local network interfaces: %s" % e)
247 249 _load_ips_dumb()
248 250
249 251
250 252 @_requires_ips
251 253 def local_ips():
252 254 """return the IP addresses that point to this machine"""
253 255 return LOCAL_IPS
254 256
255 257 @_requires_ips
256 258 def public_ips():
257 259 """return the IP addresses for this machine that are visible to other machines"""
258 260 return PUBLIC_IPS
259 261
260 262 @_requires_ips
261 263 def localhost():
262 264 """return ip for localhost (almost always 127.0.0.1)"""
263 265 return LOCALHOST
264 266
265 267 @_requires_ips
266 268 def is_local_ip(ip):
267 269 """does `ip` point to this machine?"""
268 270 return ip in LOCAL_IPS
269 271
270 272 @_requires_ips
271 273 def is_public_ip(ip):
272 274 """is `ip` a publicly visible address?"""
273 275 return ip in PUBLIC_IPS
General Comments 0
You need to be logged in to leave comments. Login now