def connect(self, host, port, stream_id): if self.state != self.READY: raise RuntimeError("PRUDP socket may only be used once") logger.info("Connecting to %s:%i:%i", host, port, stream_id) self.state = self.CONNECTING self.remote_port = stream_id self.local_port = 0xF if self.transport_type == self.settings.TRANSPORT_WEBSOCKET: self.local_port = 0x1F if not self.sock.connect(host, port): logger.error("Socket connection failed") self.state = self.DISCONNECTED return False self.timeout_event = scheduler.add_timeout(self.handle_silence_timeout, self.silence_timeout) self.socket_event = scheduler.add_socket(self.handle_recv, self.sock) syn_packet = PRUDPPacket(TYPE_SYN, FLAG_NEED_ACK) syn_packet.signature = bytes(self.packet_encoder.signature_size()) self.send_packet(syn_packet) if not self.wait_ack(syn_packet): logger.error("SYN handshake failed") return False self.local_session_id = random.randint(0, 0xFF) if self.transport_type == self.settings.TRANSPORT_UDP: self.source_signature = secrets.token_bytes( self.packet_encoder.signature_size()) else: self.source_signature = hmac.HMAC( self.signature_key, self.signature_key + self.target_signature).digest() connect_packet = PRUDPPacket(TYPE_CONNECT, FLAG_RELIABLE | FLAG_NEED_ACK) connect_packet.signature = self.source_signature connect_packet.payload = self.build_connection_request() self.send_packet(connect_packet) if not self.wait_ack(connect_packet): logger.error("CONNECT handshake failed") return False self.packet_id_in += 1 self.ping_event = scheduler.add_timeout(self.handle_ping, self.ping_timeout, True) logger.info("PRUDP connection OK") self.state = self.CONNECTED return True
def send(self, station, message, delay=1, limit=0): ack_id = next(self.ack_id) & 0xFFFFFFFF message.payload += struct.pack(">I", ack_id) handle = ResendMessage(station, message, ack_id, limit) self.messages[ack_id] = handle self.transport.send(station, message) handle.timeout = scheduler.add_timeout(self.handle_timeout, delay, True, handle) self.wait_ack(message)
def send_packet(self, packet): packet.packet_id = next(self.packet_id_out) logger.debug("(%i) Sending packet: %s", self.session_id, packet) if packet.flags & FLAG_NEED_ACK: event = scheduler.add_timeout(self.handle_ack_timeout, self.resend_timeout, param=(packet, 0)) self.ack_events[packet.packet_id] = event self.send_packet_raw(packet)
def handle_ack_timeout(self, param): packet, counter = param if counter < 3: logger.debug("(%i) Resending packet: %s", self.session_id, packet) self.send_packet_raw(packet) event = scheduler.add_timeout(self.handle_ack_timeout, self.resend_timeout, param=(packet, counter+1)) self.ack_events[packet.packet_id] = event else: logger.error("Packet timed out") self.state = self.DISCONNECTED del self.ack_events[packet.packet_id] self.remove_events()
def send(self, station, message, interval=.5, limit=10): self.ack_id = (self.ack_id + 1) & 0xFFFFFFFF if self.ack_id == 0: self.ack_id += 1 message.payload += struct.pack(">I", self.ack_id) resend = ResendingMessage(station, message, self.ack_id, limit) resend.event = scheduler.add_timeout(self.handle_timeout, interval, True, resend) self.packets[self.ack_id] = resend self.transport.send(station, message)
def accept(self, stream_id): if self.state != self.READY: raise RuntimeError("PRUDP socket may only be used once") self.state = self.ACCEPTING self.local_port = stream_id self.timeout_event = scheduler.add_timeout(self.handle_silence_timeout, self.silence_timeout) self.socket_event = scheduler.add_socket(self.handle_recv, self.sock) next(self.packet_id_out) while self.state == self.ACCEPTING: scheduler.update() return self.state == self.CONNECTED
def handle_timeout(self, param): packet, counter = param key = (packet.type, packet.stream_id, packet.packet_id) if counter < self.resend_limit: logger.debug("Resending packet: %s" %packet) self.sock.send(self.packet_encoder.encode(packet)) event = scheduler.add_timeout(self.handle_timeout, self.resend_timeout, param=(packet, counter+1)) block = self.ack_events[key][1] self.ack_events[key] = (event, block) else: logger.error("Packet timed out: %s" %packet) del self.ack_events[key] self.failure()
def connect(self, info): target_ip = info.public_station.address.address.host my_ip = self.backend.public_station["address"] target = info.public_station if target_ip == my_ip: target = info.local_station rvcid = target.rvcid if rvcid == self.session.rvcid: self.results[rvcid] = self.RESULT_OK else: self.results[rvcid] = self.RESULT_NONE self.timeouts[rvcid] = scheduler.add_timeout( self.handle_timeout, 8, False, rvcid) self.pending_nat.append(rvcid) self.nat_mgr.start_nat_traversal(target.to_station_url())
def send(self, packet, block=False): if not packet.flags & FLAG_ACK: packet.packet_id = self.sequence_mgr.assign(packet) packet.payload = self.message_encoder.encode(packet) logger.debug("Sending packet: %s", packet) self.sock.send(self.packet_encoder.encode(packet)) if packet.flags & FLAG_RELIABLE or packet.type == TYPE_SYN: if packet.flags & FLAG_NEED_ACK: key = (packet.type, packet.stream_id, packet.packet_id) event = scheduler.add_timeout(self.handle_timeout, self.resend_timeout, param=(packet, 0)) self.ack_events[key] = (event, block) if block: while key in self.ack_events: scheduler.update() if key in self.ack_packets: return self.ack_packets.pop(key)
def send(self, data, delay=0.5): limit = (self.transport.size_limit() & ~7) - 0x18 fragments = (len(data) - 1) // limit for i in range(fragments): flags = 1 if i == fragments - 1: #Last fragment flags |= 2 chunk = data[i * limit:(i + 1) * limit] payload = struct.pack(">HHIIIQ", flags, len(chunk), 0, self.packet_id_out, self.packet_id_in, self.early_packets) + chunk message = ReliableMessage(payload, self.packet_id_out, delay) message.timeout = scheduler.add_timeout(self.handle_timeout, delay, True, message) self.messages[self.packet_id_out] = message self.send_raw(payload) self.packet_id_out += 1
def start_ping(self): self.ping_event = scheduler.add_timeout(self.handle_ping, self.ping_timeout, True)
def handle_station_joined(self, station): self.keep_alive_send[station.index] = \ scheduler.add_timeout(self.send, 1, True, station)
def connect(self, host, port, payload=b""): if self.state != self.DISCONNECTED: raise RuntimeError("Socket was not disconnected") logger.info("Connecting to %s:%i", host, port) self.state = self.CONNECTING self.encryption.set_key(self.DEFAULT_KEY) self.secure_key = b"" self.server_signature = b"" self.client_signature = b"" self.connect_response = b"" self.packets = [] self.packet_queue = {} self.fragment_buffer = b"" self.packet_id_out = itertools.count() self.packet_id_in = 1 self.session_id = 0 self.packet_encoder.reset() if self.transport_type == self.settings.TRANSPORT_UDP: self.s = socket.Socket(socket.TYPE_UDP) elif self.transport_type == self.settings.TRANSPORT_TCP: self.s = socket.Socket(socket.TYPE_TCP) else: self.s = websocket.WebSocket() if not self.s.connect(host, port): logger.error("Socket connection failed") self.state = self.DISCONNECTED return False self.ack_events = {} self.ping_event = None self.timeout_event = scheduler.add_timeout(self.handle_silence_timeout, self.silence_timeout) self.socket_event = scheduler.add_socket(self.handle_recv, self.s) self.send_packet(self.syn_packet) if not self.wait_ack(self.syn_packet): logger.error("PRUDP connection failed") return False self.session_id = random.randint(0, 0xFF) if self.transport_type == self.settings.TRANSPORT_UDP: self.client_signature = bytes([ random.randint(0, 0xFF) for i in range(self.packet_encoder.signature_size()) ]) else: self.client_signature = hmac.HMAC( self.signature_key, self.signature_key + self.server_signature).digest() self.connect_packet.signature = self.client_signature self.connect_packet.payload = payload self.send_packet(self.connect_packet) if not self.wait_ack(self.connect_packet): logger.error("PRUDP connection failed") return False self.ping_event = scheduler.add_timeout(self.handle_ping, self.ping_timeout, True) logger.info("PRUDP connection OK") self.state = self.CONNECTED return True