Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2# This file is part of Xpra. 

3# Copyright (C) 2013-2020 Antoine Martin <antoine@xpra.org> 

4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

5# later version. See the file COPYING for details. 

6 

7# taken from the code I wrote for winswitch 

8 

9import socket 

10import sys 

11 

12from xpra.os_util import WIN32 

13from xpra.log import Logger 

14 

15log = Logger("network", "util") 

16 

17 

18netifaces_version = 0 

19_netifaces = None 

20def import_netifaces() -> object: 

21 global _netifaces, netifaces_version 

22 if _netifaces is None: 

23 try: 

24 import netifaces #@UnresolvedImport 

25 log("netifaces loaded sucessfully") 

26 _netifaces = netifaces 

27 netifaces_version = netifaces.version #@UndefinedVariable 

28 except ImportError: 

29 _netifaces = False 

30 log.warn("Warning: the python netifaces package is missing") 

31 return _netifaces 

32 

33iface_ipmasks = {} 

34bind_IPs = None 

35 

36 

37def get_free_tcp_port() -> int: 

38 s = socket.socket() 

39 s.bind(('', 0)) 

40 port = s.getsockname()[1] 

41 s.close() 

42 return port 

43 

44 

45def get_interfaces(): 

46 netifaces = import_netifaces() 

47 if not netifaces: 

48 return [] 

49 return netifaces.interfaces() #@UndefinedVariable pylint: disable=no-member 

50 

51def get_interfaces_addresses() -> dict: 

52 d = {} 

53 netifaces = import_netifaces() 

54 if netifaces: 

55 for iface in get_interfaces(): 

56 d[iface] = netifaces.ifaddresses(iface) #@UndefinedVariable pylint: disable=no-member 

57 return d 

58 

59def get_interface(address): 

60 for iface, idefs in get_interfaces_addresses().items(): 

61 #ie: { 

62 # 17: [{'broadcast': u'ff:ff:ff:ff:ff:ff', 'addr': u'00:e0:4c:68:46:a6'}], 

63 # 2: [{'broadcast': u'192.168.1.255', 'netmask': u'255.255.255.0', 'addr': u'192.168.1.7'}], 

64 # 10: [{'netmask': u'ffff:ffff:ffff:ffff::/64', 'addr': u'fe80::6c45:655:c59e:92a1%eth0'}] 

65 #} 

66 for _itype, defs in idefs.items(): 

67 #ie: itype=2, defs=[{'broadcast': u'192.168.1.255', 'netmask': u'255.255.255.0', 'addr': u'192.168.1.7'}] 

68 for props in defs: 

69 if props.get("addr")==address: 

70 return iface 

71 return None 

72 

73def get_gateways() -> dict: 

74 netifaces = import_netifaces() 

75 if not netifaces: 

76 return {} 

77 #versions older than 0.10.5 can crash when calling gateways() 

78 #https://bitbucket.org/al45tair/netifaces/issues/15/gateways-function-crash-segmentation-fault 

79 if netifaces.version<'0.10.5': #@UndefinedVariable pylint: disable=no-member 

80 return {} 

81 try: 

82 d = netifaces.gateways() #@UndefinedVariable pylint: disable=no-member 

83 AF_NAMES = {} 

84 for k in dir(netifaces): 

85 if k.startswith("AF_"): 

86 v = getattr(netifaces, k) 

87 AF_NAMES[v] = k[3:] 

88 gateways = {} 

89 for family, gws in d.items(): 

90 if family=="default": 

91 continue 

92 gateways[AF_NAMES.get(family, family)] = gws 

93 return gateways 

94 except Exception: 

95 log("get_gateways() failed", exc_info=True) 

96 return {} 

97 

98def get_bind_IPs(): 

99 global bind_IPs 

100 if not bind_IPs: 

101 netifaces = import_netifaces() 

102 if netifaces: 

103 bind_IPs = do_get_bind_IPs() 

104 else: 

105 bind_IPs = ["127.0.0.1"] 

106 return bind_IPs 

107 

108def do_get_bind_IPs(): 

109 global iface_ipmasks 

110 ips = [] 

111 netifaces = import_netifaces() 

112 assert netifaces 

113 ifaces = netifaces.interfaces() #@UndefinedVariable pylint: disable=no-member 

114 log("ifaces=%s", ifaces) 

115 for iface in ifaces: 

116 if_ipmasks = [] 

117 try: 

118 ipmasks = do_get_bind_ifacemask(iface) 

119 for ipmask in ipmasks: 

120 (ip,_) = ipmask 

121 if ip not in ips: 

122 ips.append(ip) 

123 if ipmask not in if_ipmasks: 

124 if_ipmasks.append(ipmask) 

125 except Exception as e: 

126 log("do_get_bind_IPs()", exc_info=True) 

127 log.error("Error parsing network interface '%s':", iface) 

128 log.error(" %s", iface, e) 

129 iface_ipmasks[iface] = if_ipmasks 

130 log("do_get_bind_IPs()=%s", ips) 

131 log("iface_ipmasks=%s", iface_ipmasks) 

132 return ips 

133 

134def do_get_bind_ifacemask(iface): 

135 ipmasks = [] 

136 netifaces = import_netifaces() 

137 assert netifaces 

138 address_types = netifaces.ifaddresses(iface) #@UndefinedVariable pylint: disable=no-member 

139 for addresses in address_types.values(): 

140 for address in addresses: 

141 if 'netmask' in address and 'addr' in address: 

142 addr = address['addr'] 

143 mask = address['netmask'] 

144 if addr!= '::1' and addr != '0.0.0.0' and addr.find("%")<0: 

145 try: 

146 socket.inet_aton(addr) 

147 ipmasks.append((addr,mask)) 

148 except Exception as e: 

149 log.error("do_get_bind_ifacemask(%s) error on %s", iface, addr, e) 

150 log("do_get_bind_ifacemask(%s)=%s", iface, ipmasks) 

151 return ipmasks 

152 

153def get_iface(ip) -> str: 

154 log("get_iface(%s)", ip) 

155 if not ip: 

156 return None 

157 if ip.find("%")>=0: 

158 iface = ip.split("%", 1)[1] 

159 if if_nametoindex: 

160 try: 

161 if_nametoindex(iface) 

162 except OSError: 

163 return None 

164 else: 

165 return iface 

166 if ip.find(":")>=0: 

167 #ipv6? 

168 return None 

169 if any(x for x in ip if (".:0123456789").find(x)<0): 

170 #extra characters, assume this is a hostname: 

171 try: 

172 v = socket.getaddrinfo(ip, None) 

173 assert len(v)>0 

174 except Exception as e: 

175 log("socket.getaddrinfo(%s, None)", ip, exc_info=True) 

176 log.error("Error: cannot revolve '%s'", ip) 

177 return None 

178 for i, x in enumerate(v): 

179 family, socktype, proto, canonname, sockaddr = x 

180 log("get_iface(%s) [%i]=%s", ip, i, (family, socktype, proto, canonname, sockaddr)) 

181 if family==socket.AF_INET: 

182 break 

183 log("get_iface(%s) sockaddr=%s", ip, sockaddr) 

184 ip = sockaddr[0] 

185 

186 ip_parts = ip.split(".") 

187 if len(ip_parts)!=4: 

188 return None 

189 

190 best_match = None 

191 get_bind_IPs() 

192 for (iface, ipmasks) in iface_ipmasks.items(): 

193 for (test_ip,mask) in ipmasks: 

194 if test_ip == ip: 

195 #exact match 

196 log("get_iface(%s)=%s", iface, ip) 

197 return iface 

198 test_ip_parts = test_ip.split(".") 

199 mask_parts = mask.split(".") 

200 if len(test_ip_parts)!=4 or len(mask_parts)!=4: 

201 log.error("incorrect ip or mask: %s/%s", test_ip, mask) 

202 match = True 

203 try: 

204 for i in (0,1,2,3): 

205 mask_part = int(mask_parts[i]) 

206 ip_part = int(ip_parts[i]) & mask_part 

207 test_ip_part = int(test_ip_parts[i]) & mask_part 

208 if ip_part!=test_ip_part: 

209 match = False 

210 break 

211 if match: 

212 best_match = iface 

213 except Exception as e: 

214 log.error("error parsing ip (%s) or its mask (%s): %s", test_ip, mask, e) 

215 log("get_iface(%s)=%s", ip, best_match) 

216 return best_match 

217 

218 

219# Found this recipe here: 

220# http://code.activestate.com/recipes/442490/ 

221if_nametoindex = None 

222if_indextoname = None 

223 

224if WIN32: # pragma: no cover 

225 def int_if_nametoindex(iface): 

226 #IPv6 addresses give us the interface as a string: 

227 #fe80:....%11, so try to convert "11" into 11 

228 try: 

229 return int(iface) 

230 except (TypeError, ValueError): 

231 return None 

232 if_nametoindex = int_if_nametoindex 

233else: 

234 if_nametoindex = socket.if_nametoindex 

235 def socket_if_indextoname(index): 

236 if index<0: 

237 return None 

238 return socket.if_indextoname(index) 

239 if_indextoname = socket_if_indextoname 

240 

241 

242net_sys_config = None 

243def get_net_sys_config(): 

244 global net_sys_config 

245 if net_sys_config is None: 

246 net_sys_config = {} 

247 if sys.platform.startswith("linux"): 

248 def stripnl(v): 

249 return str(v).rstrip("\r").rstrip("\n") 

250 def addproc(procpath, subsystem, name, conv=stripnl): 

251 assert name 

252 try: 

253 with open(procpath) as f: 

254 data = f.read() 

255 subdict = net_sys_config.setdefault(subsystem, {}) 

256 if name.find("/")>0: 

257 sub, name = name.split("/", 1) 

258 subdict = subdict.setdefault(sub, {}) 

259 for sub in ("ip", "tcp", "ipfrag", "icmp", "igmp"): 

260 if name.startswith("%s_" % sub): 

261 name = name[len(sub)+1:] 

262 subdict = subdict.setdefault(sub, {}) 

263 break 

264 subdict[name] = conv(data) 

265 except Exception as e: 

266 log("cannot read '%s': %s", procpath, e) 

267 for k in ("netdev_max_backlog", "optmem_max", "rmem_default", "rmem_max", "wmem_default", "wmem_max", "max_skb_frags", 

268 "busy_poll", "busy_read", "somaxconn"): 

269 addproc("/proc/sys/net/core/%s" % k, "core", k, int) 

270 for k in ("default_qdisc", ): 

271 addproc("/proc/sys/net/core/%s" % k, "core", k) 

272 for k in ("max_dgram_qlen", ): 

273 addproc("/proc/sys/net/unix/%s" % k, "unix", k, int) 

274 for k in ("ip_forward", "ip_forward_use_pmtu", "tcp_abort_on_overflow", "fwmark_reflect", "tcp_autocorking", "tcp_dsack", 

275 "tcp_ecn_fallback", "tcp_fack", 

276 #"tcp_l3mdev_accept", 

277 "tcp_low_latency", "tcp_no_metrics_save", "tcp_recovery", "tcp_retrans_collapse", "tcp_timestamps", 

278 "tcp_workaround_signed_windows", "tcp_thin_linear_timeouts", "tcp_thin_dupack", "ip_nonlocal_bind", 

279 "ip_dynaddr", "ip_early_demux", "icmp_echo_ignore_all", "icmp_echo_ignore_broadcasts", 

280 ): 

281 addproc("/proc/sys/net/ipv4/%s" % k, "ipv4", k, bool) 

282 for k in ("tcp_allowed_congestion_control", "tcp_available_congestion_control", "tcp_congestion_control", "tcp_early_retrans", 

283 "tcp_moderate_rcvbuf", "tcp_rfc1337", "tcp_sack", "tcp_slow_start_after_idle", "tcp_stdurg", 

284 "tcp_syncookies", "tcp_tw_recycle", "tcp_tw_reuse", "tcp_window_scaling", 

285 "icmp_ignore_bogus_error_responses", "icmp_errors_use_inbound_ifaddr"): 

286 addproc("/proc/sys/net/ipv4/%s" % k, "ipv4", k) 

287 def parsenums(v): 

288 return tuple(int(x.strip()) for x in v.split("\t") if len(x.strip())>0) 

289 for k in ("tcp_mem", "tcp_rmem", "tcp_wmem", "ip_local_port_range", "ip_local_reserved_ports", ): 

290 addproc("/proc/sys/net/ipv4/%s" % k, "ipv4", k, parsenums) 

291 for k in ("ip_default_ttl", "ip_no_pmtu_disc", "route/min_pmtu", 

292 "route/mtu_expires", "route/min_adv_mss", 

293 "ipfrag_high_thresh", "ipfrag_low_thresh", "ipfrag_time", "ipfrag_max_dist", 

294 "tcp_adv_win_scale", "tcp_app_win", "tcp_base_mss", "tcp_ecn", "tcp_fin_timeout", "tcp_frto", 

295 "tcp_invalid_ratelimit", "tcp_keepalive_time", "tcp_keepalive_probes", "tcp_keepalive_intvl", 

296 "tcp_max_orphans", "tcp_max_syn_backlog", "tcp_max_tw_buckets", 

297 "tcp_min_rtt_wlen", "tcp_mtu_probing", "tcp_probe_interval", "tcp_probe_threshold", "tcp_orphan_retries", 

298 "tcp_reordering", "tcp_max_reordering", "tcp_retries1", "tcp_retries2", "tcp_synack_retries", 

299 "tcp_fastopen", "tcp_syn_retries", "tcp_min_tso_segs", "tcp_pacing_ss_ratio", 

300 "tcp_pacing_ca_ratio", "tcp_tso_win_divisor", "tcp_notsent_lowat", 

301 "tcp_limit_output_bytes", "tcp_challenge_ack_limit", 

302 "icmp_ratelimit", "icmp_msgs_per_sec", "icmp_msgs_burst", "icmp_ratemask", 

303 "igmp_max_memberships", "igmp_max_msf", "igmp_qrv", 

304 ): 

305 addproc("/proc/sys/net/ipv4/%s" % k, "ipv4", k, int) 

306 return net_sys_config 

307 

308def get_net_config() -> dict: 

309 config = {} 

310 try: 

311 from xpra.net.bytestreams import VSOCK_TIMEOUT, SOCKET_TIMEOUT, SOCKET_NODELAY 

312 config = { 

313 "vsocket.timeout" : VSOCK_TIMEOUT, 

314 "socket.timeout" : SOCKET_TIMEOUT, 

315 } 

316 if SOCKET_NODELAY is not None: 

317 config["socket.nodelay"] = SOCKET_NODELAY 

318 except Exception: # pragma: no cover 

319 log("get_net_config()", exc_info=True) 

320 return config 

321 

322 

323def get_ssl_info(show_constants=False) -> dict: 

324 try: 

325 import ssl 

326 except ImportError as e: # pragma: no cover 

327 log("no ssl: %s", e) 

328 return {} 

329 info = {} 

330 if show_constants: 

331 protocols = dict((k,int(getattr(ssl, k))) for k in dir(ssl) if k.startswith("PROTOCOL_")) 

332 ops = dict((k,int(getattr(ssl, k))) for k in dir(ssl) if k.startswith("OP_")) 

333 vers = dict((k,int(getattr(ssl, k))) for k in dir(ssl) if k.startswith("VERIFY_")) 

334 info.update({ 

335 "protocols" : protocols, 

336 "options" : ops, 

337 "verify" : vers, 

338 }) 

339 for k,name in { 

340 "HAS_ALPN" : "alpn", 

341 "HAS_ECDH" : "ecdh", 

342 "HAS_SNI" : "sni", 

343 "HAS_NPN" : "npn", 

344 "CHANNEL_BINDING_TYPES" : "channel-binding-types", 

345 }.items(): 

346 v = getattr(ssl, k, None) 

347 if v is not None: 

348 info[name] = v 

349 for k, idef in { 

350 "" : ("version", str), 

351 "_INFO" : ("version-info", str), 

352 "_NUMBER" : ("version-number", int), 

353 }.items(): 

354 v = getattr(ssl, "OPENSSL_VERSION%s" % k, None) 

355 if v is not None: 

356 name, conv = idef 

357 info.setdefault("openssl", {})[name] = conv(v) 

358 return info 

359 

360 

361def get_network_caps() -> dict: 

362 from xpra.net.digest import get_digests 

363 from xpra.net.crypto import get_crypto_caps 

364 from xpra.net.compression import get_enabled_compressors, get_compression_caps 

365 from xpra.net.packet_encoding import get_enabled_encoders, get_packet_encoding_caps 

366 digests = get_digests() 

367 #"hmac" is the legacy name, "xor" and "des" should not be used for salt: 

368 salt_digests = tuple(x for x in digests if x not in ("hmac", "xor", "des")) 

369 caps = { 

370 "digest" : digests, 

371 "salt-digest" : salt_digests, 

372 "compressors" : get_enabled_compressors(), 

373 "encoders" : get_enabled_encoders(), 

374 } 

375 from xpra.net.common import FLUSH_HEADER 

376 if FLUSH_HEADER: 

377 caps["flush"] = True 

378 caps.update(get_crypto_caps()) 

379 caps.update(get_compression_caps()) 

380 caps.update(get_packet_encoding_caps()) 

381 return caps 

382 

383 

384def get_info() -> dict: 

385 i = get_network_caps() 

386 netifaces = import_netifaces() 

387 if netifaces: 

388 i["interfaces"] = get_interfaces() 

389 i["gateways"] = get_gateways() 

390 if "ssl" in sys.modules: 

391 ssli = get_ssl_info() 

392 ssli[""] = True 

393 i["ssl"] = ssli 

394 s = get_net_sys_config() 

395 if s: 

396 i["system"] = s 

397 i["config"] = get_net_config() 

398 paramiko = sys.modules.get("paramiko") 

399 if paramiko: 

400 i["paramiko"] = { 

401 "version" : paramiko.__version_info__, 

402 } 

403 return i 

404 

405 

406def main(): # pragma: no cover 

407 from xpra.os_util import POSIX 

408 from xpra.util import print_nested_dict, csv 

409 from xpra.platform import program_context 

410 from xpra.platform.netdev_query import get_interface_info 

411 from xpra.log import enable_color, add_debug_category, enable_debug_for 

412 with program_context("Network-Info", "Network Info"): 

413 enable_color() 

414 verbose = "-v" in sys.argv or "--verbose" in sys.argv 

415 if verbose: 

416 enable_debug_for("network") 

417 add_debug_category("network") 

418 log.enable_debug() 

419 

420 print("Network interfaces found:") 

421 netifaces = import_netifaces() 

422 for iface in get_interfaces(): 

423 if if_nametoindex: 

424 print("* %s (index=%s)" % (iface.ljust(20), if_nametoindex(iface))) 

425 else: 

426 print("* %s" % iface) 

427 addresses = netifaces.ifaddresses(iface) #@UndefinedVariable pylint: disable=no-member 

428 for addr, defs in addresses.items(): 

429 if addr in (socket.AF_INET, socket.AF_INET6): 

430 for d in defs: 

431 ip = d.get("addr") 

432 if ip: 

433 stype = { 

434 socket.AF_INET : "IPv4", 

435 socket.AF_INET6 : "IPv6", 

436 }[addr] 

437 print(" * %s: %s" % (stype, ip)) 

438 if POSIX: 

439 from xpra.net.socket_util import create_tcp_socket 

440 try: 

441 sock = create_tcp_socket(ip, 0) 

442 sockfd = sock.fileno() 

443 info = get_interface_info(sockfd, iface) 

444 if info: 

445 print(" %s" % info) 

446 finally: 

447 sock.close() 

448 if not POSIX: 

449 info = get_interface_info(0, iface) 

450 if info: 

451 print(" %s" % info) 

452 

453 from xpra.os_util import bytestostr 

454 def pver(v): 

455 if isinstance(v, (tuple, list)): 

456 s = "" 

457 lastx = None 

458 for x in v: 

459 if lastx is not None: 

460 #dot seperated numbers 

461 if isinstance(lastx, int): 

462 s += "." 

463 else: 

464 s += ", " 

465 s += bytestostr(x) 

466 lastx = x 

467 return s 

468 if isinstance(v, bytes): 

469 v = bytestostr(v) 

470 if isinstance(v, str) and v.startswith("v"): 

471 return v[1:] 

472 return str(v) 

473 

474 print("Gateways found:") 

475 for gt,idefs in get_gateways().items(): 

476 print("* %s" % gt) #ie: "INET" 

477 for i, idef in enumerate(idefs): 

478 if isinstance(idef, (list, tuple)): 

479 print(" [%i] %s" % (i, csv(idef))) 

480 continue 

481 

482 print("") 

483 print("Protocol Capabilities:") 

484 netcaps = get_network_caps() 

485 netif = {"" : bool(netifaces)} 

486 if netifaces_version: 

487 netif["version"] = netifaces_version 

488 netcaps["netifaces"] = netif 

489 print_nested_dict(netcaps, vformat=pver) 

490 

491 print("") 

492 print("Network Config:") 

493 print_nested_dict(get_net_config()) 

494 

495 net_sys = get_net_sys_config() 

496 if net_sys: 

497 print("") 

498 print("Network System Config:") 

499 print_nested_dict(net_sys) 

500 

501 print("") 

502 print("SSL:") 

503 print_nested_dict(get_ssl_info(True)) 

504 

505 try: 

506 from xpra.net.crypto import crypto_backend_init, get_crypto_caps 

507 crypto_backend_init() 

508 ccaps = get_crypto_caps() 

509 if ccaps: 

510 print("") 

511 print("Crypto Capabilities:") 

512 print_nested_dict(ccaps) 

513 except Exception as e: 

514 print("No Crypto:") 

515 print(" %s" % e) 

516 return 0 

517 

518 

519if __name__ == "__main__": # pragma: no cover 

520 main()