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