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