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# This file is part of Xpra. 

2# Copyright (C) 2017-2021 Antoine Martin <antoine@xpra.org> 

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

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

5 

6import os.path 

7import socket 

8from time import sleep 

9from ctypes import Structure, c_uint8, sizeof 

10 

11from xpra.scripts.config import InitException, InitExit, TRUE_OPTIONS 

12from xpra.exit_codes import ( 

13 EXIT_SSL_FAILURE, EXIT_SSL_CERTIFICATE_VERIFY_FAILURE, 

14 EXIT_SERVER_ALREADY_EXISTS, EXIT_SOCKET_CREATION_ERROR, 

15 ) 

16from xpra.net.bytestreams import set_socket_timeout, pretty_socket 

17from xpra.os_util import ( 

18 getuid, get_username_for_uid, get_groups, get_group_id, 

19 path_permission_info, monotonic_time, umask_context, WIN32, OSX, POSIX, 

20 parse_encoded_bin_data, 

21 ) 

22from xpra.util import ( 

23 envint, envbool, csv, parse_simple_dict, 

24 ellipsizer, 

25 DEFAULT_PORT, 

26 ) 

27from xpra.make_thread import start_thread 

28 

29#what timeout value to use on the socket probe attempt: 

30WAIT_PROBE_TIMEOUT = envint("XPRA_WAIT_PROBE_TIMEOUT", 6) 

31GROUP = os.environ.get("XPRA_GROUP", "xpra") 

32PEEK_TIMEOUT = envint("XPRA_PEEK_TIMEOUT", 1) 

33PEEK_TIMEOUT_MS = envint("XPRA_PEEK_TIMEOUT_MS", PEEK_TIMEOUT*1000) 

34PEEK_SIZE = envint("XPRA_PEEK_SIZE", 8192) 

35 

36SOCKET_DIR_MODE = num = int(os.environ.get("XPRA_SOCKET_DIR_MODE", "775"), 8) 

37SOCKET_DIR_GROUP = os.environ.get("XPRA_SOCKET_DIR_GROUP", GROUP) 

38 

39 

40network_logger = None 

41def get_network_logger(): 

42 global network_logger 

43 if not network_logger: 

44 from xpra.log import Logger 

45 network_logger = Logger("network") 

46 return network_logger 

47 

48 

49def create_unix_domain_socket(sockpath, socket_permissions=0o600): 

50 assert POSIX 

51 #convert this to a umask! 

52 umask = (0o777-socket_permissions) & 0o777 

53 listener = socket.socket(socket.AF_UNIX) 

54 listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

55 #bind the socket, using umask to set the correct permissions 

56 with umask_context(umask): 

57 listener.bind(sockpath) 

58 try: 

59 inode = os.stat(sockpath).st_ino 

60 except OSError: 

61 inode = -1 

62 #set to the "xpra" group if we are a member of it, or if running as root: 

63 uid = getuid() 

64 username = get_username_for_uid(uid) 

65 groups = get_groups(username) 

66 if uid==0 or GROUP in groups: 

67 group_id = get_group_id(GROUP) 

68 if group_id>=0: 

69 try: 

70 os.lchown(sockpath, -1, group_id) 

71 except Exception as e: 

72 log = get_network_logger() 

73 log.warn("Warning: failed to set '%s' group ownership", GROUP) 

74 log.warn(" on socket '%s':", sockpath) 

75 log.warn(" %s", e) 

76 #don't know why this doesn't work: 

77 #os.fchown(listener.fileno(), -1, group_id) 

78 def cleanup_socket(): 

79 log = get_network_logger() 

80 try: 

81 cur_inode = os.stat(sockpath).st_ino 

82 except OSError: 

83 log.info("socket '%s' already deleted", sockpath) 

84 return 

85 delpath = sockpath 

86 log("cleanup_socket '%s', original inode=%s, new inode=%s", sockpath, inode, cur_inode) 

87 if cur_inode==inode: 

88 log.info("removing unix domain socket '%s'", delpath) 

89 try: 

90 os.unlink(delpath) 

91 except OSError: 

92 pass 

93 return listener, cleanup_socket 

94 

95def has_dual_stack() -> bool: 

96 """ 

97 Return True if kernel allows creating a socket which is able to 

98 listen for both IPv4 and IPv6 connections. 

99 If *sock* is provided the check is made against it. 

100 """ 

101 try: 

102 assert socket.AF_INET6 and socket.IPPROTO_IPV6 and socket.IPV6_V6ONLY 

103 except AttributeError: 

104 return False 

105 try: 

106 import contextlib 

107 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 

108 with contextlib.closing(sock): 

109 sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False) 

110 return True 

111 except socket.error: 

112 return False 

113 

114def hosts(host_str): 

115 if host_str=="*": 

116 if has_dual_stack(): 

117 #IPv6 will also listen for IPv4: 

118 return ["::"] 

119 #no dual stack, so we have to listen on both IPv4 and IPv6 explicitly: 

120 return ["0.0.0.0", "::"] 

121 return [host_str] 

122 

123def add_listen_socket(socktype, sock, info, new_connection_cb, new_udp_connection_cb=None, options=None): 

124 log = get_network_logger() 

125 log("add_listen_socket(%s, %s, %s, %s, %s, %s)", 

126 socktype, sock, info, new_connection_cb, new_udp_connection_cb, options) 

127 try: 

128 #ugly that we have different ways of starting sockets, 

129 #TODO: abstract this into the socket class 

130 if socktype=="named-pipe": 

131 #named pipe listener uses a thread: 

132 sock.new_connection_cb = new_connection_cb 

133 sock.start() 

134 return None 

135 sources = [] 

136 if socktype=="udp": 

137 assert new_udp_connection_cb, "UDP sockets cannot be handled here" 

138 new_udp_connection_cb(sock) 

139 else: 

140 from gi.repository import GLib 

141 sock.listen(5) 

142 def io_in_cb(sock, flags): 

143 log("io_in_cb(%s, %s)", sock, flags) 

144 return new_connection_cb(socktype, sock) 

145 source = GLib.io_add_watch(sock, GLib.PRIORITY_DEFAULT, GLib.IO_IN, io_in_cb) 

146 sources.append(source) 

147 upnp_cleanup = [] 

148 if socktype in ("udp", "tcp", "ws", "wss", "ssh", "ssl"): 

149 upnp = (options or {}).get("upnp", "no") 

150 if upnp.lower() in TRUE_OPTIONS: 

151 from xpra.net.upnp import upnp_add 

152 upnp_cleanup.append(upnp_add(socktype, info, options)) 

153 def cleanup(): 

154 for source in tuple(sources): 

155 GLib.source_remove(source) 

156 sources.remove(source) 

157 for c in upnp_cleanup: 

158 if c: 

159 start_thread(c, "pnp-cleanup-%s" % c, daemon=True) 

160 return cleanup 

161 except Exception as e: 

162 log("add_listen_socket(%s, %s)", socktype, sock, exc_info=True) 

163 log.error("Error: failed to listen on %s socket %s:", socktype, info or sock) 

164 log.error(" %s", e) 

165 return None 

166 

167 

168def accept_connection(socktype, listener, timeout=None, socket_options=None): 

169 log = get_network_logger() 

170 try: 

171 sock, address = listener.accept() 

172 except socket.error as e: 

173 log("rejecting new connection on %s", listener, exc_info=True) 

174 log.error("Error: cannot accept new connection:") 

175 log.error(" %s", e) 

176 return None 

177 #log("peercred(%s)=%s", sock, get_peercred(sock)) 

178 try: 

179 peername = sock.getpeername() 

180 except OSError: 

181 peername = address 

182 sock.settimeout(timeout) 

183 sockname = sock.getsockname() 

184 from xpra.net.bytestreams import SocketConnection 

185 conn = SocketConnection(sock, sockname, address, peername, socktype, None, socket_options) 

186 log("accept_connection(%s, %s, %s)=%s", listener, socktype, timeout, conn) 

187 return conn 

188 

189def peek_connection(conn, timeout=PEEK_TIMEOUT_MS, size=PEEK_SIZE): 

190 log = get_network_logger() 

191 log("peek_connection(%s, %i)", conn, timeout) 

192 peek_data = b"" 

193 start = monotonic_time() 

194 elapsed = 0 

195 set_socket_timeout(conn, PEEK_TIMEOUT_MS*1000) 

196 while elapsed<=timeout: 

197 try: 

198 peek_data = conn.peek(size) 

199 if peek_data: 

200 break 

201 except OSError: 

202 log("peek_connection(%s, %i) failed", conn, timeout, exc_info=True) 

203 except ValueError: 

204 log("peek_connection(%s, %i) failed", conn, timeout, exc_info=True) 

205 break 

206 sleep(timeout/4000.0) 

207 elapsed = int(1000*(monotonic_time()-start)) 

208 log("peek: elapsed=%s, timeout=%s", elapsed, timeout) 

209 log("socket %s peek: got %i bytes", conn, len(peek_data)) 

210 return peek_data 

211 

212 

213POSIX_TCP_INFO = ( 

214 ("state", c_uint8), 

215 ) 

216def get_sockopt_tcp_info(sock, TCP_INFO, attributes=POSIX_TCP_INFO): 

217 def get_tcpinfo_class(fields): 

218 class TCPInfo(Structure): 

219 _fields_ = tuple(fields) 

220 def __repr__(self): 

221 return "TCPInfo(%s)" % self.getdict() 

222 def getdict(self): 

223 return {k[0] : getattr(self, k[0]) for k in self._fields_} 

224 return TCPInfo 

225 #calculate full structure size with all the fields defined: 

226 tcpinfo_class = get_tcpinfo_class(attributes) 

227 tcpinfo_size = sizeof(tcpinfo_class) 

228 data = sock.getsockopt(socket.SOL_TCP, TCP_INFO, tcpinfo_size) 

229 data_size = len(data) 

230 #but only define fields in the ctypes.Structure 

231 #if they are actually present in the returned data: 

232 while tcpinfo_size>data_size: 

233 #trim it down: 

234 attributes = attributes[:-1] 

235 tcpinfo_class = get_tcpinfo_class(attributes) 

236 tcpinfo_size = sizeof(tcpinfo_class) 

237 log = get_network_logger() 

238 if tcpinfo_size==0: 

239 log("getsockopt(SOL_TCP, TCP_INFO, %i) no data", tcpinfo_size) 

240 return {} 

241 #log("total size=%i for fields: %s", size, csv(fdef[0] for fdef in fields)) 

242 try: 

243 tcpinfo = tcpinfo_class.from_buffer_copy(data[:tcpinfo_size]) 

244 except ValueError as e: 

245 log("getsockopt(SOL_TCP, TCP_INFO, %i)", tcpinfo_size, exc_info=True) 

246 log("TCPInfo fields=%s", csv(tcpinfo_class._fields_)) 

247 log.warn("Warning: failed to get TCP_INFO for %s", sock) 

248 log.warn(" %s", e) 

249 return {} 

250 d = tcpinfo.getdict() 

251 log("getsockopt(SOL_TCP, TCP_INFO, %i)=%s", tcpinfo_size, d) 

252 return d 

253 

254 

255 

256def guess_packet_type(data): 

257 if not data: 

258 return None 

259 if data[0]==ord("P"): 

260 from xpra.net.header import ( 

261 unpack_header, HEADER_SIZE, 

262 FLAGS_RENCODE, FLAGS_YAML, 

263 LZ4_FLAG, LZO_FLAG, BROTLI_FLAG, 

264 ) 

265 header = data.ljust(HEADER_SIZE, b"\0") 

266 _, protocol_flags, compression_level, packet_index, data_size = unpack_header(header) 

267 #this normally used on the first packet, so the packet index should be 0 

268 #and I don't think we can make packets smaller than 8 bytes, 

269 #even with packet name aliases and rencode 

270 #(and aliases should not be defined for the initial packet anyway) 

271 if packet_index==0 and 8<data_size<256*1024*1024: 

272 rencode = bool(protocol_flags & FLAGS_RENCODE) 

273 yaml = bool(protocol_flags & FLAGS_YAML) 

274 lz4 = bool(protocol_flags & LZ4_FLAG) 

275 lzo = bool(protocol_flags & LZO_FLAG) 

276 brotli = bool(protocol_flags & BROTLI_FLAG) 

277 #rencode and yaml are mutually exclusive, 

278 #so are the compressors 

279 compressors = sum((lz4, lzo, brotli)) 

280 if not (rencode and yaml) and not compressors>1: 

281 #if compression is enabled, the compression level must be set: 

282 if not compressors or compression_level>0: 

283 pass #return "xpra" 

284 if data[:4]==b"SSH-": 

285 return "ssh" 

286 if data[0]==0x16: 

287 return "ssl" 

288 line1 = data.splitlines()[0] 

289 if line1.find(b"HTTP/")>0 or line1.split(b" ")[0] in (b"GET", b"POST"): 

290 return "http" 

291 if line1.lower().startswith(b"<!doctype html") or line1.lower().startswith(b"<html"): 

292 return "http" 

293 return None 

294 

295 

296def create_sockets(opts, error_cb): 

297 bind_tcp = parse_bind_ip(opts.bind_tcp) 

298 bind_udp = parse_bind_ip(opts.bind_udp) 

299 bind_ssl = parse_bind_ip(opts.bind_ssl, 443) 

300 bind_ssh = parse_bind_ip(opts.bind_ssh, 22) 

301 bind_ws = parse_bind_ip(opts.bind_ws, 80) 

302 bind_wss = parse_bind_ip(opts.bind_wss, 443) 

303 bind_rfb = parse_bind_ip(opts.bind_rfb, 5900) 

304 bind_vsock = parse_bind_vsock(opts.bind_vsock) 

305 

306 sockets = {} 

307 

308 min_port = int(opts.min_port) 

309 def add_tcp_socket(socktype, host_str, iport, options): 

310 if iport!=0 and iport<min_port: 

311 error_cb("invalid %s port number %i (minimum value is %i)" % (socktype, iport, min_port)) 

312 for host in hosts(host_str): 

313 sock = setup_tcp_socket(host, iport, socktype) 

314 host, iport = sock[2] 

315 sockets[sock] = options 

316 def add_udp_socket(socktype, host_str, iport, options): 

317 if iport!=0 and iport<min_port: 

318 error_cb("invalid %s port number %i (minimum value is %i)" % (socktype, iport, min_port)) 

319 for host in hosts(host_str): 

320 sock = setup_udp_socket(host, iport, socktype) 

321 host, iport = sock[2] 

322 sockets[sock] = options 

323 # Initialize the TCP sockets before the display, 

324 # That way, errors won't make us kill the Xvfb 

325 # (which may not be ours to kill at that point) 

326 ssh_upgrades = opts.ssh_upgrade 

327 if ssh_upgrades: 

328 try: 

329 from xpra.net.ssh import nogssapi_context 

330 with nogssapi_context(): 

331 import paramiko 

332 assert paramiko 

333 except ImportError as e: 

334 from xpra.log import Logger 

335 sshlog = Logger("ssh") 

336 sshlog("import paramiko", exc_info=True) 

337 sshlog.error("Error: cannot enable SSH socket upgrades:") 

338 sshlog.error(" %s", e) 

339 ssh_upgrades = False 

340 log = get_network_logger() 

341 for socktype, defs in { 

342 "tcp" : bind_tcp, 

343 "ssl" : bind_ssl, 

344 "ssh" : bind_ssh, 

345 "ws" : bind_ws, 

346 "wss" : bind_wss, 

347 "rfb" : bind_rfb, 

348 }.items(): 

349 log("setting up %s sockets: %s", socktype, csv(defs.items())) 

350 for (host, iport), options in defs.items(): 

351 add_tcp_socket(socktype, host, iport, options) 

352 log("setting up UDP sockets: %s", csv(bind_udp.items())) 

353 for (host, iport), options in bind_udp.items(): 

354 add_udp_socket("udp", host, iport, options) 

355 log("setting up vsock sockets: %s", csv(bind_vsock.items())) 

356 for (cid, iport), options in bind_vsock.items(): 

357 sock = setup_vsock_socket(cid, iport) 

358 sockets[sock] = options 

359 

360 # systemd socket activation: 

361 if POSIX and not OSX: 

362 try: 

363 from xpra.platform.xposix.sd_listen import get_sd_listen_sockets 

364 except ImportError as e: 

365 log("no systemd socket activation: %s", e) 

366 else: 

367 sd_sockets = get_sd_listen_sockets() 

368 log("systemd sockets: %s", sd_sockets) 

369 for stype, sock, addr in sd_sockets: 

370 sock = setup_sd_listen_socket(stype, sock, addr) 

371 sockets[sock] = {} 

372 log("%s : %s", (stype, [addr]), sock) 

373 return sockets 

374 

375def create_tcp_socket(host, iport): 

376 log = get_network_logger() 

377 if host.find(":")<0: 

378 listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

379 sockaddr = (host, iport) 

380 else: 

381 if host.startswith("[") and host.endswith("]"): 

382 host = host[1:-1] 

383 assert socket.has_ipv6, "specified an IPv6 address but this is not supported" 

384 res = socket.getaddrinfo(host, iport, socket.AF_INET6, socket.SOCK_STREAM, 0, socket.SOL_TCP) 

385 log("socket.getaddrinfo(%s, %s, AF_INET6, SOCK_STREAM, 0, SOL_TCP)=%s", host, iport, res) 

386 listener = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) 

387 sockaddr = res[0][-1] 

388 listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

389 log("%s.bind(%s)", listener, sockaddr) 

390 listener.bind(sockaddr) 

391 return listener 

392 

393def setup_tcp_socket(host, iport, socktype="tcp"): 

394 log = get_network_logger() 

395 try: 

396 tcp_socket = create_tcp_socket(host, iport) 

397 except Exception as e: 

398 log("create_tcp_socket%s", (host, iport), exc_info=True) 

399 raise InitExit(EXIT_SOCKET_CREATION_ERROR, 

400 "failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e)) from None 

401 def cleanup_tcp_socket(): 

402 log.info("closing %s socket '%s:%s'", socktype.lower(), host, iport) 

403 try: 

404 tcp_socket.close() 

405 except OSError: 

406 pass 

407 if iport==0: 

408 iport = tcp_socket.getsockname()[1] 

409 log.info("allocated %s port %i on %s", socktype, iport, host) 

410 log("%s: %s:%s : %s", socktype, host, iport, socket) 

411 log.info("created %s socket '%s:%s'", socktype, host, iport) 

412 return socktype, tcp_socket, (host, iport), cleanup_tcp_socket 

413 

414def create_udp_socket(host, iport): 

415 if host.find(":")<0: 

416 listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

417 sockaddr = (host, iport) 

418 else: 

419 assert socket.has_ipv6, "specified an IPv6 address but this is not supported" 

420 res = socket.getaddrinfo(host, iport, socket.AF_INET6, socket.SOCK_DGRAM) 

421 listener = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 

422 sockaddr = res[0][-1] 

423 listener.bind(sockaddr) 

424 return listener 

425 

426def setup_udp_socket(host, iport, socktype="udp"): 

427 log = get_network_logger() 

428 try: 

429 udp_socket = create_udp_socket(host, iport) 

430 except Exception as e: 

431 log("create_udp_socket%s", (host, iport), exc_info=True) 

432 raise InitExit(EXIT_SOCKET_CREATION_ERROR, 

433 "failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e)) from None 

434 def cleanup_udp_socket(): 

435 log.info("closing %s socket %s:%s", socktype, host, iport) 

436 try: 

437 udp_socket.close() 

438 except OSError: 

439 pass 

440 if iport==0: 

441 iport = udp_socket.getsockname()[1] 

442 log.info("allocated UDP port %i for %s", iport, host) 

443 log("%s: %s:%s : %s", socktype, host, iport, socket) 

444 log.info("created UDP socket %s:%s", host, iport) 

445 return socktype, udp_socket, (host, iport), cleanup_udp_socket 

446 

447 

448def parse_bind_ip(bind_ip, default_port=DEFAULT_PORT): 

449 ip_sockets = {} 

450 if bind_ip: 

451 for spec in bind_ip: 

452 parts = spec.split(",", 1) 

453 ip_port = parts[0] 

454 if ":" not in spec: 

455 raise InitException("port must be specified as [HOST]:PORT") 

456 host, port = ip_port.rsplit(":", 1) 

457 if host == "": 

458 host = "127.0.0.1" 

459 if not port: 

460 iport = default_port 

461 elif port=="0": 

462 iport = 0 

463 else: 

464 try: 

465 iport = int(port) 

466 assert 0<iport<2**16 

467 except (TypeError, ValueError): 

468 raise InitException("invalid port number: %s" % port) from None 

469 options = {} 

470 if len(parts)==2: 

471 options = parse_simple_dict(parts[1]) 

472 ip_sockets[(host, iport)] = options 

473 return ip_sockets 

474 

475def setup_vsock_socket(cid, iport): 

476 log = get_network_logger() 

477 try: 

478 from xpra.net.vsock import bind_vsocket #@UnresolvedImport 

479 vsock_socket = bind_vsocket(cid=cid, port=iport) 

480 except Exception as e: 

481 raise InitExit(EXIT_SOCKET_CREATION_ERROR, 

482 "failed to setup vsock socket on %s:%s %s" % (cid, iport, e)) from None 

483 def cleanup_vsock_socket(): 

484 log.info("closing vsock socket %s:%s", cid, iport) 

485 try: 

486 vsock_socket.close() 

487 except OSError: 

488 pass 

489 return "vsock", vsock_socket, (cid, iport), cleanup_vsock_socket 

490 

491def parse_bind_vsock(bind_vsock): 

492 vsock_sockets = {} 

493 if bind_vsock: 

494 from xpra.scripts.main import parse_vsock 

495 for spec in bind_vsock: 

496 parts = spec.split(",", 1) 

497 cid, iport = parse_vsock(parts[0]) 

498 options = {} 

499 if len(parts)==2: 

500 options = parse_simple_dict(parts[1]) 

501 vsock_sockets[(cid, iport)] = options 

502 return vsock_sockets 

503 

504def setup_sd_listen_socket(stype, sock, addr): 

505 log = get_network_logger() 

506 def cleanup_sd_listen_socket(): 

507 log.info("closing sd listen socket %s", pretty_socket(addr)) 

508 try: 

509 sock.close() 

510 except OSError: 

511 pass 

512 return stype, sock, addr, cleanup_sd_listen_socket 

513 

514 

515def normalize_local_display_name(local_display_name): 

516 pos = local_display_name.find(":") 

517 if pos<0: 

518 after_sc = local_display_name 

519 local_display_name = ":" + local_display_name 

520 else: 

521 after_sc = local_display_name[pos+1:] 

522 if WIN32 or OSX: 

523 if after_sc.isalnum(): 

524 return local_display_name 

525 raise Exception("non alphanumeric character in display name '%s'" % local_display_name) 

526 #we used to strip the screen from the display string, ie: ":0.0" -> ":0" 

527 #but now we allow it.. (untested!) 

528 for char in after_sc: 

529 assert char in "0123456789.", "invalid character in display name '%s': %s" % (local_display_name, char) 

530 return local_display_name 

531 

532 

533def setup_local_sockets(bind, socket_dir, socket_dirs, display_name, clobber, 

534 mmap_group="auto", socket_permissions="600", username="", uid=0, gid=0): 

535 log = get_network_logger() 

536 log("setup_local_sockets%s", (bind, socket_dir, socket_dirs, display_name, clobber, 

537 mmap_group, socket_permissions, username, uid, gid)) 

538 if not bind: 

539 return {} 

540 if not socket_dir and (not socket_dirs or (len(socket_dirs)==1 and not socket_dirs[0])): 

541 if WIN32: 

542 socket_dirs = [""] 

543 else: 

544 raise InitExit(EXIT_SOCKET_CREATION_ERROR, 

545 "at least one socket directory must be set to use unix domain sockets") 

546 from xpra.platform.dotxpra import DotXpra, norm_makepath 

547 dotxpra = DotXpra(socket_dir or socket_dirs[0], socket_dirs, username, uid, gid) 

548 if display_name is not None and not WIN32: 

549 display_name = normalize_local_display_name(display_name) 

550 defs = {} 

551 try: 

552 sockpaths = {} 

553 log("setup_local_sockets: bind=%s, dotxpra=%s", bind, dotxpra) 

554 for b in bind: 

555 if b in ("none", ""): 

556 continue 

557 parts = b.split(",") 

558 sockpath = parts[0] 

559 options = {} 

560 if len(parts)==2: 

561 options = parse_simple_dict(parts[1]) 

562 if sockpath=="auto": 

563 assert display_name is not None 

564 for sockpath in dotxpra.norm_socket_paths(display_name): 

565 sockpaths[sockpath] = options 

566 log("sockpaths(%s)=%s (uid=%i, gid=%i)", display_name, sockpaths, uid, gid) 

567 else: 

568 sockpath = dotxpra.osexpand(sockpath) 

569 if os.path.isabs(sockpath): 

570 pass 

571 elif sockpath.endswith("/") or (os.path.exists(sockpath) and os.path.isdir(sockpath)): 

572 assert display_name is not None 

573 sockpath = os.path.abspath(sockpath) 

574 if not os.path.exists(sockpath): 

575 os.makedirs(sockpath) 

576 sockpath = norm_makepath(sockpath, display_name) 

577 else: 

578 sockpath = dotxpra.socket_path(sockpath) 

579 sockpaths[sockpath] = options 

580 assert sockpaths, "no socket paths to try for %s" % b 

581 #expand and remove duplicate paths: 

582 tmp = {} 

583 for tsp, options in sockpaths.items(): 

584 sockpath = dotxpra.osexpand(tsp) 

585 if sockpath in tmp: 

586 log.warn("Warning: skipping duplicate bind path %s", sockpath) 

587 continue 

588 tmp[sockpath] = options 

589 sockpaths = tmp 

590 log("sockpaths=%s", sockpaths) 

591 #create listeners: 

592 if WIN32: 

593 from xpra.platform.win32.namedpipes.listener import NamedPipeListener 

594 from xpra.platform.win32.dotxpra import PIPE_PATH 

595 for sockpath, options in sockpaths.items(): 

596 npl = NamedPipeListener(sockpath) 

597 ppath = sockpath 

598 if ppath.startswith(PIPE_PATH): 

599 ppath = ppath[len(PIPE_PATH):] 

600 log.info("created named pipe '%s'", ppath) 

601 defs[("named-pipe", npl, sockpath, npl.stop)] = options 

602 else: 

603 def checkstate(sockpath, state): 

604 if state not in (DotXpra.DEAD, DotXpra.UNKNOWN): 

605 if state==DotXpra.INACCESSIBLE: 

606 raise InitException("An xpra server is already running at %s\n" % (sockpath,)) 

607 raise InitExit(EXIT_SERVER_ALREADY_EXISTS, 

608 "You already have an xpra server running at %s\n" 

609 " (did you want 'xpra upgrade'?)" 

610 % (sockpath,)) 

611 #remove exisiting sockets if clobber is set, 

612 #otherwise verify there isn't a server already running 

613 #and create the directories for the sockets: 

614 unknown = [] 

615 for sockpath in sockpaths: 

616 if clobber and os.path.exists(sockpath): 

617 os.unlink(sockpath) 

618 else: 

619 state = dotxpra.get_server_state(sockpath, 1) 

620 log("state(%s)=%s", sockpath, state) 

621 checkstate(sockpath, state) 

622 if state==dotxpra.UNKNOWN: 

623 unknown.append(sockpath) 

624 d = os.path.dirname(sockpath) 

625 try: 

626 kwargs = {} 

627 if d in ("/var/run/xpra", "/run/xpra"): 

628 #this is normally done by tmpfiles.d, 

629 #but we may need to do it ourselves in some cases: 

630 kwargs["mode"] = SOCKET_DIR_MODE 

631 xpra_gid = get_group_id(SOCKET_DIR_GROUP) 

632 if xpra_gid>0: 

633 kwargs["gid"] = xpra_gid 

634 log("creating sockdir=%s, kwargs=%s" % (d, kwargs)) 

635 dotxpra.mksockdir(d, **kwargs) 

636 log("%s permission mask: %s", d, oct(os.stat(d).st_mode)) 

637 except Exception as e: 

638 log.warn("Warning: failed to create socket directory '%s'", d) 

639 log.warn(" %s", e) 

640 del e 

641 #wait for all the unknown ones: 

642 log("sockets in unknown state: %s", unknown) 

643 if unknown: 

644 #re-probe them using threads so we can do them in parallel: 

645 threads = [] 

646 def timeout_probe(sockpath): 

647 #we need a loop because "DEAD" sockets may return immediately 

648 #(ie: when the server is starting up) 

649 start = monotonic_time() 

650 while monotonic_time()-start<WAIT_PROBE_TIMEOUT: 

651 state = dotxpra.get_server_state(sockpath, WAIT_PROBE_TIMEOUT) 

652 log("timeout_probe() get_server_state(%s)=%s", sockpath, state) 

653 if state not in (DotXpra.UNKNOWN, DotXpra.DEAD): 

654 break 

655 sleep(1) 

656 log.warn("Warning: some of the sockets are in an unknown state:") 

657 for sockpath in unknown: 

658 log.warn(" %s", sockpath) 

659 t = start_thread(timeout_probe, "probe-%s" % sockpath, daemon=True, args=(sockpath,)) 

660 threads.append(t) 

661 log.warn(" please wait as we allow the socket probing to timeout") 

662 #wait for all the threads to do their job: 

663 for t in threads: 

664 t.join(WAIT_PROBE_TIMEOUT+1) 

665 if sockpaths: 

666 #now we can re-check quickly: 

667 #(they should all be DEAD or UNKNOWN): 

668 for sockpath in sockpaths: 

669 state = dotxpra.get_server_state(sockpath, 1) 

670 log("state(%s)=%s", sockpath, state) 

671 checkstate(sockpath, state) 

672 try: 

673 if os.path.exists(sockpath): 

674 os.unlink(sockpath) 

675 except OSError: 

676 pass 

677 #socket permissions: 

678 if mmap_group.lower() in TRUE_OPTIONS: 

679 #when using the mmap group option, use '660' 

680 sperms = 0o660 

681 else: 

682 #parse octal mode given as config option: 

683 try: 

684 if isinstance(socket_permissions, int): 

685 sperms = socket_permissions 

686 else: 

687 #assume octal string: 

688 sperms = int(socket_permissions, 8) 

689 assert 0<=sperms<=0o777, "invalid socket permission value %s" % oct(sperms) 

690 except ValueError: 

691 raise ValueError("invalid socket permissions "+ 

692 "(must be an octal number): '%s'" % socket_permissions) from None 

693 #now try to create all the sockets: 

694 for sockpath, options in sockpaths.items(): 

695 #create it: 

696 try: 

697 sock, cleanup_socket = create_unix_domain_socket(sockpath, sperms) 

698 log.info("created unix domain socket '%s'", sockpath) 

699 defs[("unix-domain", sock, sockpath, cleanup_socket)] = options 

700 except Exception as e: 

701 handle_socket_error(sockpath, sperms, e) 

702 del e 

703 except Exception: 

704 for sock, cleanup_socket in defs.items(): 

705 try: 

706 cleanup_socket() 

707 except Exception as e: 

708 log.error("Error cleaning up socket %s:", sock) 

709 log.error(" %s", e) 

710 del e 

711 raise 

712 return defs 

713 

714def handle_socket_error(sockpath, sperms, e): 

715 log = get_network_logger() 

716 log("socket creation error", exc_info=True) 

717 if sockpath.startswith("/var/run/xpra") or sockpath.startswith("/run/xpra"): 

718 log.info("cannot create group socket '%s'", sockpath) 

719 log.info(" %s", e) 

720 dirname = sockpath[:sockpath.find("xpra")+len("xpra")] 

721 if not os.path.exists(dirname): 

722 log.info(" %s does not exist", dirname) 

723 #only show extra information if the socket permissions 

724 #would have been accessible by the group: 

725 elif POSIX and (sperms & 0o40): 

726 uid = getuid() 

727 username = get_username_for_uid(uid) 

728 groups = get_groups(username) 

729 log.info(" user '%s' is a member of groups: %s", username, csv(groups) or "no groups!") 

730 if "xpra" not in groups: 

731 log.info(" add 'xpra' group membership to enable group socket sharing") 

732 for x in path_permission_info(dirname): 

733 log.info(" %s", x) 

734 elif sockpath.startswith("/var/run/user") or sockpath.startswith("/run/user"): 

735 log.warn("Warning: cannot create socket '%s':", sockpath) 

736 log.warn(" %s", e) 

737 run_user = sockpath.split("/user")[0]+"/user" 

738 if not os.path.exists(run_user): 

739 log.warn(" %s does not exist", run_user) 

740 else: 

741 log.warn(" ($XDG_RUNTIME_DIR has not been created?)") 

742 else: 

743 log.error("Error: failed to create socket '%s':", sockpath) 

744 log.error(" %s", e) 

745 raise InitExit(EXIT_SOCKET_CREATION_ERROR, 

746 "failed to create socket %s" % sockpath) 

747 

748 

749#warn just once: 

750MDNS_WARNING = False 

751def mdns_publish(display_name, listen_on, text_dict=None): 

752 global MDNS_WARNING 

753 if MDNS_WARNING is True: 

754 return () 

755 from xpra.log import Logger 

756 log = Logger("mdns") 

757 log("mdns_publish%s", (display_name, listen_on, text_dict)) 

758 try: 

759 from xpra.net import mdns 

760 assert mdns 

761 from xpra.net.mdns import XPRA_MDNS_TYPE, RFB_MDNS_TYPE 

762 PREFER_ZEROCONF = envbool("XPRA_PREFER_ZEROCONF", WIN32 or OSX) 

763 if PREFER_ZEROCONF: 

764 from xpra.net.mdns.zeroconf_publisher import ZeroconfPublishers as MDNSPublishers, get_interface_index 

765 else: 

766 from xpra.net.mdns.avahi_publisher import AvahiPublishers as MDNSPublishers, get_interface_index 

767 except ImportError as e: 

768 MDNS_WARNING = True 

769 log("mdns import failure", exc_info=True) 

770 log.warn("Warning: failed to load the mdns publisher") 

771 try: 

772 einfo = str(e) 

773 except Exception: 

774 einfo = str(type(e)) 

775 log.warn(" %s", einfo) 

776 log.warn(" either install the 'python-avahi' module") 

777 log.warn(" or use the 'mdns=no' option") 

778 return () 

779 d = dict(text_dict or {}) 

780 #ensure we don't have duplicate interfaces: 

781 f_listen_on = {} 

782 for host, port in listen_on: 

783 f_listen_on[(get_interface_index(host), port)] = (host, port) 

784 try: 

785 name = socket.gethostname() 

786 except OSError: 

787 name = "Xpra" 

788 if display_name and not (OSX or WIN32): 

789 name += " %s" % display_name 

790 mode = d.get("mode", "tcp") 

791 service_type = {"rfb" : RFB_MDNS_TYPE}.get(mode, XPRA_MDNS_TYPE) 

792 index = 0 

793 aps = [] 

794 for host, port in listen_on: 

795 sn = name 

796 mode_str = mode 

797 if index>0: 

798 mode_str = "%s-%i" % (mode, index+1) 

799 if mode not in ("tcp", "rfb"): 

800 sn += " (%s)" % mode_str 

801 listen = ( (host, port), ) 

802 index += 1 

803 aps.append(MDNSPublishers(listen, sn, service_type=service_type, text_dict=d)) 

804 return aps 

805 

806 

807SSL_ATTRIBUTES = ( 

808 "cert", "key", "ca_certs", "ca_data", 

809 "protocol", 

810 "client_verify_mode", "server_verify_mode", "verify_flags", 

811 "check_hostname", "server_hostname", 

812 "options", "ciphers", 

813 ) 

814 

815def get_ssl_attributes(opts, server_side=True, overrides=None): 

816 args = { 

817 "server_side" : server_side, 

818 } 

819 for attr in SSL_ATTRIBUTES: 

820 ssl_attr = "ssl_%s" % attr #ie: "ssl_ca_certs" 

821 option = ssl_attr.replace("_", "-") #ie: "ssl-ca-certs" 

822 v = (overrides or {}).get(option) 

823 if v is None: 

824 v = getattr(opts, ssl_attr) 

825 args[attr] = v 

826 return args 

827 

828def ssl_wrap_socket(sock, **kwargs): 

829 fn = get_ssl_wrap_socket_fn(**kwargs) 

830 return fn(sock) 

831 

832def get_ssl_wrap_socket_fn(cert=None, key=None, ca_certs=None, ca_data=None, 

833 protocol="TLSv1_2", 

834 client_verify_mode="optional", server_verify_mode="required", verify_flags="X509_STRICT", 

835 check_hostname=False, server_hostname=None, 

836 options="ALL,NO_COMPRESSION", ciphers="DEFAULT", 

837 server_side=True): 

838 if server_side and not cert: 

839 raise InitException("you must specify an 'ssl-cert' file to use ssl sockets") 

840 if server_side: 

841 verify_mode = client_verify_mode 

842 else: 

843 verify_mode = server_verify_mode 

844 from xpra.log import Logger 

845 ssllog = Logger("ssl") 

846 ssllog("get_ssl_wrap_socket_fn%s", (cert, key, ca_certs, ca_data, 

847 protocol, 

848 client_verify_mode, server_verify_mode, verify_flags, 

849 check_hostname, server_hostname, 

850 options, ciphers, 

851 server_side)) 

852 import ssl 

853 ssllog(" verify_mode for server_side=%s : %s", server_side, verify_mode) 

854 #ca-certs: 

855 if ca_certs=="default": 

856 ca_certs = None 

857 ssllog(" ca_certs=%s", ca_certs) 

858 #parse verify-mode: 

859 ssl_cert_reqs = getattr(ssl, "CERT_%s" % verify_mode.upper(), None) 

860 if ssl_cert_reqs is None: 

861 values = [k[len("CERT_"):].lower() for k in dir(ssl) if k.startswith("CERT_")] 

862 raise InitException("invalid ssl-server-verify-mode '%s', must be one of: %s" % (verify_mode, csv(values))) 

863 ssllog(" cert_reqs=%#x", ssl_cert_reqs) 

864 #parse protocol: 

865 proto = getattr(ssl, "PROTOCOL_%s" % (protocol.upper().replace("V", "v")), None) 

866 if proto is None: 

867 values = [k[len("PROTOCOL_"):] for k in dir(ssl) if k.startswith("PROTOCOL_")] 

868 raise InitException("invalid ssl-protocol '%s', must be one of: %s" % (protocol, csv(values))) 

869 ssllog(" protocol=%#x", proto) 

870 #ca_data may be hex encoded: 

871 ca_data = parse_encoded_bin_data(ca_data) 

872 ssllog(" cadata=%s", ellipsizer(ca_data)) 

873 

874 kwargs = { 

875 "server_side" : server_side, 

876 "do_handshake_on_connect" : False, 

877 "suppress_ragged_eofs" : True, 

878 } 

879 #parse ssl-verify-flags as CSV: 

880 ssl_verify_flags = 0 

881 for x in verify_flags.split(","): 

882 x = x.strip() 

883 if not x: 

884 continue 

885 v = getattr(ssl, "VERIFY_"+x.upper(), None) 

886 if v is None: 

887 raise InitException("invalid ssl verify-flag: %s" % x) 

888 ssl_verify_flags |= v 

889 ssllog(" verify_flags=%#x", ssl_verify_flags) 

890 #parse ssl-options as CSV: 

891 ssl_options = 0 

892 for x in options.split(","): 

893 x = x.strip() 

894 if not x: 

895 continue 

896 v = getattr(ssl, "OP_"+x.upper(), None) 

897 if v is None: 

898 raise InitException("invalid ssl option: %s" % x) 

899 ssl_options |= v 

900 ssllog(" options=%#x", ssl_options) 

901 

902 context = ssl.SSLContext(proto) 

903 context.set_ciphers(ciphers) 

904 context.verify_mode = ssl_cert_reqs 

905 context.verify_flags = ssl_verify_flags 

906 context.options = ssl_options 

907 ssllog(" cert=%s, key=%s", cert, key) 

908 if cert: 

909 SSL_KEY_PASSWORD = os.environ.get("XPRA_SSL_KEY_PASSWORD") 

910 ssllog("context.load_cert_chain%s", (cert or None, key or None, SSL_KEY_PASSWORD)) 

911 context.load_cert_chain(certfile=cert or None, keyfile=key or None, password=SSL_KEY_PASSWORD) 

912 if ssl_cert_reqs!=ssl.CERT_NONE: 

913 if server_side: 

914 purpose = ssl.Purpose.CLIENT_AUTH #@UndefinedVariable 

915 else: 

916 purpose = ssl.Purpose.SERVER_AUTH #@UndefinedVariable 

917 context.check_hostname = check_hostname 

918 ssllog(" check_hostname=%s, server_hostname=%s", check_hostname, server_hostname) 

919 if context.check_hostname: 

920 if not server_hostname: 

921 raise InitException("ssl error: check-hostname is set but server-hostname is not") 

922 kwargs["server_hostname"] = server_hostname 

923 ssllog(" load_default_certs(%s)", purpose) 

924 context.load_default_certs(purpose) 

925 

926 if not ca_certs or ca_certs.lower()=="default": 

927 ssllog(" using default certs") 

928 #load_default_certs already calls set_default_verify_paths() 

929 elif not os.path.exists(ca_certs): 

930 raise InitException("invalid ssl-ca-certs file or directory: %s" % ca_certs) 

931 elif os.path.isdir(ca_certs): 

932 ssllog(" loading ca certs from directory '%s'", ca_certs) 

933 context.load_verify_locations(capath=ca_certs) 

934 else: 

935 ssllog(" loading ca certs from file '%s'", ca_certs) 

936 assert os.path.isfile(ca_certs), "'%s' is not a valid ca file" % ca_certs 

937 context.load_verify_locations(cafile=ca_certs) 

938 #handle cadata: 

939 if ca_data: 

940 #PITA: because of a bug in the ssl module, we can't pass cadata, 

941 #so we use a temporary file instead: 

942 import tempfile 

943 with tempfile.NamedTemporaryFile(prefix='cadata') as f: 

944 ssllog(" loading cadata '%s'", ellipsizer(ca_data)) 

945 ssllog(" using temporary file '%s'", f.name) 

946 f.file.write(ca_data) 

947 f.file.flush() 

948 context.load_verify_locations(cafile=f.name) 

949 elif check_hostname and not server_side: 

950 ssllog("cannot check hostname client side with verify mode %s", verify_mode) 

951 wrap_socket = context.wrap_socket 

952 def do_wrap_socket(tcp_socket): 

953 assert tcp_socket 

954 ssllog("do_wrap_socket(%s)", tcp_socket) 

955 if WIN32: 

956 #on win32, setting the tcp socket to blocking doesn't work? 

957 #we still hit the following errors that we need to retry: 

958 from xpra.net import bytestreams 

959 bytestreams.CAN_RETRY_EXCEPTIONS = (ssl.SSLWantReadError, ssl.SSLWantWriteError) 

960 else: 

961 tcp_socket.setblocking(True) 

962 try: 

963 ssl_sock = wrap_socket(tcp_socket, **kwargs) 

964 except Exception as e: 

965 ssllog.debug("wrap_socket(%s, %s)", tcp_socket, kwargs, exc_info=True) 

966 SSLEOFError = getattr(ssl, "SSLEOFError", None) 

967 if SSLEOFError and isinstance(e, SSLEOFError): 

968 return None 

969 raise InitExit(EXIT_SSL_FAILURE, "Cannot wrap socket %s: %s" % (tcp_socket, e)) 

970 if not server_side: 

971 try: 

972 ssl_sock.do_handshake(True) 

973 except Exception as e: 

974 ssllog.debug("do_handshake", exc_info=True) 

975 SSLEOFError = getattr(ssl, "SSLEOFError", None) 

976 if SSLEOFError and isinstance(e, SSLEOFError): 

977 return None 

978 status = EXIT_SSL_FAILURE 

979 SSLCertVerificationError = getattr(ssl, "SSLCertVerificationError", None) 

980 if SSLCertVerificationError and isinstance(e, SSLCertVerificationError): 

981 try: 

982 msg = e.args[1].split(":", 2)[2] 

983 except (ValueError, IndexError): 

984 msg = str(e) 

985 status = EXIT_SSL_CERTIFICATE_VERIFY_FAILURE 

986 #ssllog.warn("host failed SSL verification: %s", msg) 

987 else: 

988 msg = str(e) 

989 raise InitExit(status, "SSL handshake failed: %s" % msg) 

990 return ssl_sock 

991 return do_wrap_socket