TCPInterface.py
1 # Reticulum License 2 # 3 # Copyright (c) 2016-2025 Mark Qvist 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # - The Software shall not be used in any kind of system which includes amongst 13 # its functions the ability to purposefully do harm to human beings. 14 # 15 # - The Software shall not be used, directly or indirectly, in the creation of 16 # an artificial intelligence, machine learning or language model training 17 # dataset, including but not limited to any use that contributes to the 18 # training or development of such a model or algorithm. 19 # 20 # - The above copyright notice and this permission notice shall be included in 21 # all copies or substantial portions of the Software. 22 # 23 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 # SOFTWARE. 30 31 from RNS.Interfaces.Interface import Interface 32 import socketserver 33 import threading 34 import platform 35 import socket 36 import time 37 import sys 38 import os 39 import RNS 40 41 class TCPInterface(): 42 HW_MTU = 262144 43 44 class HDLC(): 45 FLAG = 0x7E 46 ESC = 0x7D 47 ESC_MASK = 0x20 48 49 @staticmethod 50 def escape(data): 51 data = data.replace(bytes([HDLC.ESC]), bytes([HDLC.ESC, HDLC.ESC^HDLC.ESC_MASK])) 52 data = data.replace(bytes([HDLC.FLAG]), bytes([HDLC.ESC, HDLC.FLAG^HDLC.ESC_MASK])) 53 return data 54 55 class KISS(): 56 FEND = 0xC0 57 FESC = 0xDB 58 TFEND = 0xDC 59 TFESC = 0xDD 60 CMD_DATA = 0x00 61 CMD_UNKNOWN = 0xFE 62 63 @staticmethod 64 def escape(data): 65 data = data.replace(bytes([0xdb]), bytes([0xdb, 0xdd])) 66 data = data.replace(bytes([0xc0]), bytes([0xdb, 0xdc])) 67 return data 68 69 class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 70 pass 71 72 class ThreadingTCP6Server(socketserver.ThreadingMixIn, socketserver.TCPServer): 73 address_family = socket.AF_INET6 74 75 class TCPClientInterface(Interface): 76 BITRATE_GUESS = 10*1000*1000 77 DEFAULT_IFAC_SIZE = 16 78 AUTOCONFIGURE_MTU = True 79 80 RECONNECT_WAIT = 5 81 RECONNECT_MAX_TRIES = None 82 83 # TCP socket options 84 TCP_USER_TIMEOUT = 24 85 TCP_PROBE_AFTER = 5 86 TCP_PROBE_INTERVAL = 2 87 TCP_PROBES = 12 88 89 INITIAL_CONNECT_TIMEOUT = 5 90 SYNCHRONOUS_START = True 91 92 I2P_USER_TIMEOUT = 45 93 I2P_PROBE_AFTER = 10 94 I2P_PROBE_INTERVAL = 9 95 I2P_PROBES = 5 96 97 def __init__(self, owner, configuration, connected_socket=None): 98 super().__init__() 99 100 c = Interface.get_config_obj(configuration) 101 name = c["name"] 102 target_ip = c["target_host"] if "target_host" in c and c["target_host"] != None else None 103 target_port = int(c["target_port"]) if "target_port" in c and c["target_host"] != None else None 104 kiss_framing = False 105 if "kiss_framing" in c and c.as_bool("kiss_framing") == True: 106 kiss_framing = True 107 i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False 108 connect_timeout = c.as_int("connect_timeout") if "connect_timeout" in c else None 109 max_reconnect_tries = c.as_int("max_reconnect_tries") if "max_reconnect_tries" in c else None 110 111 self.HW_MTU = TCPInterface.HW_MTU 112 self.IN = True 113 self.OUT = False 114 self.socket = None 115 self.parent_interface = None 116 self.name = name 117 self.initiator = False 118 self.reconnecting = False 119 self.never_connected = True 120 self.owner = owner 121 self.writing = False 122 self.online = False 123 self.detached = False 124 self.kiss_framing = kiss_framing 125 self.i2p_tunneled = i2p_tunneled 126 self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL 127 self.bitrate = TCPClientInterface.BITRATE_GUESS 128 129 if max_reconnect_tries == None: 130 self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES 131 else: 132 self.max_reconnect_tries = max_reconnect_tries 133 134 if connected_socket != None: 135 self.receives = True 136 self.target_ip = None 137 self.target_port = None 138 self.socket = connected_socket 139 140 if platform.system() == "Linux": 141 self.set_timeouts_linux() 142 elif platform.system() == "Darwin": 143 self.set_timeouts_osx() 144 145 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 146 147 elif target_ip != None and target_port != None: 148 self.receives = True 149 self.target_ip = target_ip 150 self.target_port = target_port 151 self.initiator = True 152 153 if connect_timeout != None: 154 self.connect_timeout = connect_timeout 155 else: 156 self.connect_timeout = TCPClientInterface.INITIAL_CONNECT_TIMEOUT 157 158 if TCPClientInterface.SYNCHRONOUS_START: 159 self.initial_connect() 160 else: 161 thread = threading.Thread(target=self.initial_connect) 162 thread.daemon = True 163 thread.start() 164 165 def initial_connect(self): 166 if not self.connect(initial=True): 167 thread = threading.Thread(target=self.reconnect) 168 thread.daemon = True 169 thread.start() 170 else: 171 thread = threading.Thread(target=self.read_loop) 172 thread.daemon = True 173 thread.start() 174 if not self.kiss_framing: 175 self.wants_tunnel = True 176 177 def set_timeouts_linux(self): 178 if not self.i2p_tunneled: 179 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(TCPClientInterface.TCP_USER_TIMEOUT * 1000)) 180 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 181 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER)) 182 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(TCPClientInterface.TCP_PROBE_INTERVAL)) 183 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(TCPClientInterface.TCP_PROBES)) 184 185 else: 186 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, int(TCPClientInterface.I2P_USER_TIMEOUT * 1000)) 187 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 188 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER)) 189 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, int(TCPClientInterface.I2P_PROBE_INTERVAL)) 190 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, int(TCPClientInterface.I2P_PROBES)) 191 192 def set_timeouts_osx(self): 193 if hasattr(socket, "TCP_KEEPALIVE"): 194 TCP_KEEPIDLE = socket.TCP_KEEPALIVE 195 else: 196 TCP_KEEPIDLE = 0x10 197 198 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 199 200 if not self.i2p_tunneled: 201 self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.TCP_PROBE_AFTER)) 202 else: 203 self.socket.setsockopt(socket.IPPROTO_TCP, TCP_KEEPIDLE, int(TCPClientInterface.I2P_PROBE_AFTER)) 204 205 def detach(self): 206 self.online = False 207 if self.socket != None: 208 if hasattr(self.socket, "close"): 209 if callable(self.socket.close): 210 self.detached = True 211 212 try: 213 if self.socket != None: 214 self.socket.shutdown(socket.SHUT_RDWR) 215 except Exception as e: 216 RNS.log("Error while shutting down socket for "+str(self)+": "+str(e)) 217 218 try: 219 if self.socket != None: 220 self.socket.close() 221 except Exception as e: 222 RNS.log("Error while closing socket for "+str(self)+": "+str(e)) 223 224 self.socket = None 225 226 def connect(self, initial=False): 227 try: 228 if initial: 229 RNS.log("Establishing TCP connection for "+str(self)+"...", RNS.LOG_DEBUG) 230 231 address_info = socket.getaddrinfo(self.target_ip, self.target_port, proto=socket.IPPROTO_TCP)[0] 232 address_family = address_info[0] 233 target_address = address_info[4] 234 235 self.socket = socket.socket(address_family, socket.SOCK_STREAM) 236 self.socket.settimeout(TCPClientInterface.INITIAL_CONNECT_TIMEOUT) 237 self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 238 self.socket.connect(target_address) 239 self.socket.settimeout(None) 240 self.online = True 241 242 if initial: 243 RNS.log("TCP connection for "+str(self)+" established", RNS.LOG_DEBUG) 244 245 except Exception as e: 246 if initial: 247 RNS.log("Initial connection for "+str(self)+" could not be established: "+str(e), RNS.LOG_ERROR) 248 RNS.log("Leaving unconnected and retrying connection in "+str(TCPClientInterface.RECONNECT_WAIT)+" seconds.", RNS.LOG_ERROR) 249 return False 250 251 else: 252 raise e 253 254 if platform.system() == "Linux": 255 self.set_timeouts_linux() 256 elif platform.system() == "Darwin": 257 self.set_timeouts_osx() 258 259 self.online = True 260 self.writing = False 261 self.never_connected = False 262 263 return True 264 265 266 def reconnect(self): 267 if self.initiator: 268 if not self.reconnecting: 269 self.reconnecting = True 270 attempts = 0 271 while not self.online: 272 time.sleep(TCPClientInterface.RECONNECT_WAIT) 273 attempts += 1 274 275 if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries: 276 RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR) 277 self.teardown() 278 break 279 280 try: 281 self.connect() 282 283 except Exception as e: 284 RNS.log("Connection attempt for "+str(self)+" failed: "+str(e), RNS.LOG_DEBUG) 285 286 if not self.never_connected: 287 RNS.log("Reconnected socket for "+str(self)+".", RNS.LOG_INFO) 288 289 self.reconnecting = False 290 thread = threading.Thread(target=self.read_loop) 291 thread.daemon = True 292 thread.start() 293 if not self.kiss_framing: 294 RNS.Transport.synthesize_tunnel(self) 295 296 else: 297 RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR) 298 raise IOError("Attempt to reconnect on a non-initiator TCP interface") 299 300 def process_incoming(self, data): 301 if self.online and not self.detached: 302 self.rxb += len(data) 303 if hasattr(self, "parent_interface") and self.parent_interface != None: 304 self.parent_interface.rxb += len(data) 305 306 self.owner.inbound(data, self) 307 308 def process_outgoing(self, data): 309 if self.online and not self.detached: 310 # while self.writing: 311 # time.sleep(0.01) 312 313 try: 314 self.writing = True 315 316 if self.kiss_framing: 317 data = bytes([KISS.FEND])+bytes([KISS.CMD_DATA])+KISS.escape(data)+bytes([KISS.FEND]) 318 else: 319 data = bytes([HDLC.FLAG])+HDLC.escape(data)+bytes([HDLC.FLAG]) 320 321 self.socket.sendall(data) 322 self.writing = False 323 self.txb += len(data) 324 if hasattr(self, "parent_interface") and self.parent_interface != None: 325 self.parent_interface.txb += len(data) 326 327 except Exception as e: 328 RNS.log("Exception occurred while transmitting via "+str(self)+", tearing down interface", RNS.LOG_ERROR) 329 RNS.log("The contained exception was: "+str(e), RNS.LOG_ERROR) 330 self.teardown() 331 332 333 def read_loop(self): 334 try: 335 in_frame = False 336 escape = False 337 frame_buffer = b"" 338 data_in = b"" 339 data_buffer = b"" 340 341 while True: 342 if self.socket: data_in = self.socket.recv(4096) 343 else: data_in = b"" 344 if len(data_in) > 0: 345 if self.kiss_framing: 346 # Read loop for KISS framing 347 pointer = 0 348 while pointer < len(data_in): 349 byte = data_in[pointer] 350 pointer += 1 351 if (in_frame and byte == KISS.FEND and command == KISS.CMD_DATA): 352 in_frame = False 353 self.process_incoming(data_buffer) 354 elif (byte == KISS.FEND): 355 in_frame = True 356 command = KISS.CMD_UNKNOWN 357 data_buffer = b"" 358 elif (in_frame and len(data_buffer) < self.HW_MTU): 359 if (len(data_buffer) == 0 and command == KISS.CMD_UNKNOWN): 360 # We only support one HDLC port for now, so 361 # strip off the port nibble 362 byte = byte & 0x0F 363 command = byte 364 elif (command == KISS.CMD_DATA): 365 if (byte == KISS.FESC): 366 escape = True 367 else: 368 if (escape): 369 if (byte == KISS.TFEND): 370 byte = KISS.FEND 371 if (byte == KISS.TFESC): 372 byte = KISS.FESC 373 escape = False 374 data_buffer = data_buffer+bytes([byte]) 375 376 else: 377 # Read loop for standard HDLC framing 378 frame_buffer += data_in 379 flags_remaining = True 380 while flags_remaining: 381 frame_start = frame_buffer.find(HDLC.FLAG) 382 if frame_start != -1: 383 frame_end = frame_buffer.find(HDLC.FLAG, frame_start+1) 384 if frame_end != -1: 385 frame = frame_buffer[frame_start+1:frame_end] 386 frame = frame.replace(bytes([HDLC.ESC, HDLC.FLAG ^ HDLC.ESC_MASK]), bytes([HDLC.FLAG])) 387 frame = frame.replace(bytes([HDLC.ESC, HDLC.ESC ^ HDLC.ESC_MASK]), bytes([HDLC.ESC])) 388 if len(frame) > RNS.Reticulum.HEADER_MINSIZE: 389 self.process_incoming(frame) 390 frame_buffer = frame_buffer[frame_end:] 391 else: 392 flags_remaining = False 393 else: 394 flags_remaining = False 395 396 else: 397 self.online = False 398 if self.initiator and not self.detached: 399 RNS.log("The socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING) 400 self.reconnect() 401 else: 402 RNS.log("The socket for remote client "+str(self)+" was closed.", RNS.LOG_VERBOSE) 403 self.teardown() 404 405 break 406 407 408 except Exception as e: 409 self.online = False 410 RNS.log("An interface error occurred for "+str(self)+", the contained exception was: "+str(e), RNS.LOG_WARNING) 411 412 if self.initiator: 413 RNS.log("Attempting to reconnect...", RNS.LOG_WARNING) 414 self.reconnect() 415 else: 416 self.teardown() 417 418 def teardown(self): 419 if self.initiator and not self.detached: 420 RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR) 421 if RNS.Reticulum.panic_on_interface_error: 422 RNS.panic() 423 424 else: 425 RNS.log("The interface "+str(self)+" is being torn down.", RNS.LOG_VERBOSE) 426 427 self.online = False 428 self.OUT = False 429 self.IN = False 430 431 if hasattr(self, "parent_interface") and self.parent_interface != None: 432 while self in self.parent_interface.spawned_interfaces: 433 self.parent_interface.spawned_interfaces.remove(self) 434 435 if self in RNS.Transport.interfaces: 436 if not self.initiator: 437 RNS.Transport.interfaces.remove(self) 438 439 440 def __str__(self): 441 if ":" in self.target_ip: 442 ip_str = f"[{self.target_ip}]" 443 else: 444 ip_str = f"{self.target_ip}" 445 446 return "TCPInterface["+str(self.name)+"/"+ip_str+":"+str(self.target_port)+"]" 447 448 449 class TCPServerInterface(Interface): 450 BITRATE_GUESS = 10_000_000 451 DEFAULT_IFAC_SIZE = 16 452 AUTOCONFIGURE_MTU = True 453 454 @staticmethod 455 def get_address_for_if(name, bind_port, prefer_ipv6=False): 456 from RNS.Interfaces import netinfo 457 ifaddr = netinfo.ifaddresses(name) 458 if len(ifaddr) < 1: 459 raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for TCPServerInterface to bind to") 460 461 if (prefer_ipv6 or not netinfo.AF_INET in ifaddr) and netinfo.AF_INET6 in ifaddr: 462 bind_ip = ifaddr[netinfo.AF_INET6][0]["addr"] 463 if bind_ip.lower().startswith("fe80::"): 464 # We'll need to add the interface as scope for link-local addresses 465 return TCPServerInterface.get_address_for_host(f"{bind_ip}%{name}", bind_port, prefer_ipv6) 466 else: 467 return TCPServerInterface.get_address_for_host(bind_ip, bind_port, prefer_ipv6) 468 elif netinfo.AF_INET in ifaddr: 469 bind_ip = ifaddr[netinfo.AF_INET][0]["addr"] 470 return (bind_ip, bind_port) 471 else: 472 raise SystemError(f"No addresses available on specified kernel interface \"{name}\" for TCPServerInterface to bind to") 473 474 @staticmethod 475 def get_address_for_host(name, bind_port, prefer_ipv6=False): 476 address_infos = socket.getaddrinfo(name, bind_port, proto=socket.IPPROTO_TCP) 477 address_info = address_infos[0] 478 for entry in address_infos: 479 if prefer_ipv6 and entry[0] == socket.AF_INET6: 480 address_info = entry; break 481 elif not prefer_ipv6 and entry[0] == socket.AF_INET: 482 address_info = entry; break 483 484 if address_info[0] == socket.AF_INET6: 485 return (name, bind_port, address_info[4][2], address_info[4][3]) 486 elif address_info[0] == socket.AF_INET: 487 return (name, bind_port) 488 else: 489 raise SystemError(f"No suitable kernel interface available for address \"{name}\" for TCPServerInterface to bind to") 490 491 492 @property 493 def clients(self): 494 return len(self.spawned_interfaces) 495 496 def __init__(self, owner, configuration): 497 super().__init__() 498 499 c = Interface.get_config_obj(configuration) 500 name = c["name"] 501 device = c["device"] if "device" in c else None 502 port = int(c["port"]) if "port" in c else None 503 bindip = c["listen_ip"] if "listen_ip" in c else None 504 bindport = int(c["listen_port"]) if "listen_port" in c else None 505 i2p_tunneled = c.as_bool("i2p_tunneled") if "i2p_tunneled" in c else False 506 prefer_ipv6 = c.as_bool("prefer_ipv6") if "prefer_ipv6" in c else False 507 508 if port != None: 509 bindport = port 510 511 self.HW_MTU = TCPInterface.HW_MTU 512 513 self.online = False 514 self.spawned_interfaces = [] 515 516 self.IN = True 517 self.OUT = False 518 self.name = name 519 self.detached = False 520 521 self.i2p_tunneled = i2p_tunneled 522 self.mode = RNS.Interfaces.Interface.Interface.MODE_FULL 523 524 if bindport == None: 525 raise SystemError(f"No TCP port configured for interface \"{name}\"") 526 else: 527 self.bind_port = bindport 528 529 bind_address = None 530 if device != None: 531 bind_address = TCPServerInterface.get_address_for_if(device, self.bind_port, prefer_ipv6) 532 else: 533 if bindip == None: 534 raise SystemError(f"No TCP bind IP configured for interface \"{name}\"") 535 bind_address = TCPServerInterface.get_address_for_host(bindip, self.bind_port, prefer_ipv6) 536 537 if bind_address != None: 538 self.receives = True 539 self.bind_ip = bind_address[0] 540 541 def handlerFactory(callback): 542 def createHandler(*args, **keys): 543 return TCPInterfaceHandler(callback, *args, **keys) 544 return createHandler 545 546 self.owner = owner 547 548 if len(bind_address) == 4: 549 try: 550 ThreadingTCP6Server.allow_reuse_address = True 551 self.server = ThreadingTCP6Server(bind_address, handlerFactory(self.incoming_connection)) 552 except Exception as e: 553 RNS.log(f"Error while binding IPv6 socket for interface, the contained exception was: {e}", RNS.LOG_ERROR) 554 raise SystemError("Could not bind IPv6 socket for interface. Please check the specified \"listen_ip\" configuration option") 555 else: 556 ThreadingTCPServer.allow_reuse_address = True 557 self.server = ThreadingTCPServer(bind_address, handlerFactory(self.incoming_connection)) 558 self.server.daemon_threads = True 559 560 self.bitrate = TCPServerInterface.BITRATE_GUESS 561 562 thread = threading.Thread(target=self.server.serve_forever) 563 thread.daemon = True 564 thread.start() 565 566 self.online = True 567 568 else: 569 raise SystemError("Insufficient parameters to create TCP listener") 570 571 def incoming_connection(self, handler): 572 RNS.log("Accepting incoming TCP connection", RNS.LOG_VERBOSE) 573 spawned_configuration = {"name": "Client on "+self.name, "target_host": None, "target_port": None, "i2p_tunneled": self.i2p_tunneled} 574 spawned_interface = TCPClientInterface(self.owner, spawned_configuration, connected_socket=handler.request) 575 spawned_interface.OUT = self.OUT 576 spawned_interface.IN = self.IN 577 spawned_interface.target_ip = handler.client_address[0] 578 spawned_interface.target_port = str(handler.client_address[1]) 579 spawned_interface.parent_interface = self 580 spawned_interface.bitrate = self.bitrate 581 spawned_interface.optimise_mtu() 582 583 spawned_interface.ifac_size = self.ifac_size 584 spawned_interface.ifac_netname = self.ifac_netname 585 spawned_interface.ifac_netkey = self.ifac_netkey 586 if spawned_interface.ifac_netname != None or spawned_interface.ifac_netkey != None: 587 ifac_origin = b"" 588 if spawned_interface.ifac_netname != None: 589 ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netname.encode("utf-8")) 590 if spawned_interface.ifac_netkey != None: 591 ifac_origin += RNS.Identity.full_hash(spawned_interface.ifac_netkey.encode("utf-8")) 592 593 ifac_origin_hash = RNS.Identity.full_hash(ifac_origin) 594 spawned_interface.ifac_key = RNS.Cryptography.hkdf( 595 length=64, 596 derive_from=ifac_origin_hash, 597 salt=RNS.Reticulum.IFAC_SALT, 598 context=None 599 ) 600 spawned_interface.ifac_identity = RNS.Identity.from_bytes(spawned_interface.ifac_key) 601 spawned_interface.ifac_signature = spawned_interface.ifac_identity.sign(RNS.Identity.full_hash(spawned_interface.ifac_key)) 602 603 spawned_interface.announce_rate_target = self.announce_rate_target 604 spawned_interface.announce_rate_grace = self.announce_rate_grace 605 spawned_interface.announce_rate_penalty = self.announce_rate_penalty 606 spawned_interface.mode = self.mode 607 spawned_interface.HW_MTU = self.HW_MTU 608 spawned_interface.online = True 609 RNS.log("Spawned new TCPClient Interface: "+str(spawned_interface), RNS.LOG_VERBOSE) 610 RNS.Transport.interfaces.append(spawned_interface) 611 while spawned_interface in self.spawned_interfaces: 612 self.spawned_interfaces.remove(spawned_interface) 613 self.spawned_interfaces.append(spawned_interface) 614 spawned_interface.read_loop() 615 616 def received_announce(self, from_spawned=False): 617 if from_spawned: self.ia_freq_deque.append(time.time()) 618 619 def sent_announce(self, from_spawned=False): 620 if from_spawned: self.oa_freq_deque.append(time.time()) 621 622 def process_outgoing(self, data): 623 pass 624 625 def detach(self): 626 self.detached = True 627 self.online = False 628 if self.server != None: 629 if hasattr(self.server, "shutdown"): 630 if callable(self.server.shutdown): 631 try: 632 RNS.log("Detaching "+str(self), RNS.LOG_DEBUG) 633 self.server.shutdown() 634 self.server.server_close() 635 self.server = None 636 637 except Exception as e: 638 RNS.log("Error while shutting down server for "+str(self)+": "+str(e)) 639 640 641 def __str__(self): 642 if ":" in self.bind_ip: 643 ip_str = f"[{self.bind_ip}]" 644 else: 645 ip_str = f"{self.bind_ip}" 646 647 return "TCPServerInterface["+self.name+"/"+ip_str+":"+str(self.bind_port)+"]" 648 649 650 class TCPInterfaceHandler(socketserver.BaseRequestHandler): 651 def __init__(self, callback, *args, **keys): 652 self.callback = callback 653 socketserver.BaseRequestHandler.__init__(self, *args, **keys) 654 655 def handle(self): 656 self.callback(handler=self)