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