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