def handle_recv(self, pair): station, packet = pair message = PIAMessage() stream = StreamIn(packet.payload, self.settings) while not stream.eof(): peek = stream.peek(stream.available()) if all(x == 0xFF for x in peek): break message = message.copy() if not self.encoder.decode(stream, message): return if message.flags & 0x10: message.payload = zlib.decompress(message.payload) if station.id is None: station.id = message.station_id elif station.id != message.station_id: logger.error("Received message with wrong station id") return self.messages.append((station, message))
def parse_browse_reply(self, data, key, challenge): stream = StreamIn(data, self.settings) if stream.u8() != 1: return None size = stream.u32() data = stream.read(size) if not self.verify_challenge_reply(stream, key, challenge): return None if not stream.eof(): logger.warning("Browse reply is bigger than expected") return None stream = StreamIn(data, self.settings) try: session_info = stream.extract(LanSessionInfo) except Exception as e: logger.warning("Failed to parse LanSessionInfo: %s", e) return None if not stream.eof(): logger.warning("LanSessionInfo has unexpected size") return None return session_info
def handle_ack(self, station, data): logger.debug("Received ack") stream = StreamIn(data, self.settings) stream.skip(4) ack_id = stream.u32() self.resender.acknowledge(station, ack_id)
def decode(self, stream): if stream.settings.get("pia.version") < 51000: self.connection_info = stream.extract(StationConnectionInfo) else: substream = StreamIn(stream.read(0x3E), stream.settings) location = substream.extract(StationLocation) self.connection_info = StationConnectionInfo(location) self.index = stream.u8() stream.align(4)
def handle_session_reply(self, station, data): logger.debug("Received session reply") stream = StreamIn(data, self.settings) stream.skip(12) session_id = stream.u32() if session_id not in self.host_requests: logger.warning("Received unexpected session reply") return self.session_requests[session_id].update(stream)
def parse_browse_reply(self, data, key, challenge): stream = StreamIn(data, self.settings) if stream.u8() != 1: return None if stream.size() != 0x551: logger.warning( "Browse reply has unexpected size (expected 1361 bytes, got %i)" % stream.size()) return None size = stream.u32() if size != 0x512: logger.warning( "LanSessionInfo has unexpected size (expected 1298 bytes, got %i)" % size) return None try: session_info = stream.extract(LanSessionInfo) except Exception as e: import traceback traceback.print_exc() logger.warning("Failed to parse LanSessionInfo: %s", e) return None if not self.verify_challenge_reply(stream, key, challenge): return None return session_info
def handle_join_response(self, station, data): logger.debug("Received join response") if self.join_response_parser is None: logger.error("Received unexpected join response") return if data[1] == 0: reason = data[4] logger.error("Join request was denied: %i", reason) self.join_response_parser.error = True return stream = StreamIn(data, self.settings) stream.skip(1) if self.join_response_parser.update(stream): ack_id = stream.u32() self.station_protocol.send_ack(station, ack_id)
def handle_connection_response(self, station, data): logger.debug("Received connection response") if station.connection_state not in [ConnectionState.WAIT_RESPONSE, ConnectionState.WAIT_INVERSE_RESPONSE]: logger.error("Received unexpected connection response") return stream = StreamIn(data, self.settings) stream.skip(1) identification = self.process_connection_response(stream) if identification is not None: ack_id = stream.u32() self.send_ack(station, ack_id) station.identification_info = identification if station.connection_state == ConnectionState.WAIT_INVERSE_RESPONSE: self.send_connection_response(station) station.connection_state = ConnectionState.CONNECTED else: station.connection_state = ConnectionState.ERROR
def handle_session_request(self, station, data): logger.debug("Received session request") if len(data) != 0x10: logger.error("Session request has unexpected size") return stream = StreamIn(data, self.settings) stream.skip(12) session_id = stream.u32() session_info = self.session.get_session_info() if session_info.session_id != session_id: logger.warning("Received session request with different session id") return if not self.session.is_host(): logger.info("Ignoring session request because we aren't host") return self.send_session_reply(station, session_info)
def handle_host_reply(self, station, data): logger.debug("Received host reply") stream = StreamIn(data, self.settings) stream.skip(12) session_id = stream.u32() if self.settings.get("pia.version") < 51000: host = stream.extract(StationConnectionInfo).public else: host = stream.extract(StationLocation) if session_id not in self.host_requests: logger.warning("Received unexpected host reply") return self.host_requests[session_id] = host
def parse_browse_request(self, data): stream = StreamIn(data, self.settings) if stream.u8() != 0: logger.debug("Message is not a browse request") return None size = stream.u32() end = stream.tell() + size criteria = stream.extract(LanSessionSearchCriteria) if stream.tell() != end: logger.warning("LanSessionSearchCriteria has unexpected size") return None reply = self.parse_challenge(stream) if reply is None: return None if not stream.eof(): logger.warning("Browse request is bigger than expected") return None logger.info("Received browse request") return criteria, reply
def decode(self, data): data = self.check_signature(data) if data is None: logger.error("Invalid packet signature") return False stream = StreamIn(data, self.settings) if stream.u32() != 0x32AB9864: logger.error("Invalid packet identifier") return False if self.settings.get("pia.header_version") > 0: byte = stream.u8() encrypted = byte >> 7 version = byte & 0x7F if version != self.settings.get("pia.header_version"): logger.error("Unexpected packet version: %i", version) return False else: encryption = stream.u8() if encryption not in [1, 2]: logger.error("Invalid encryption mode") return False encrypted = encryption == 2 self.connection_id = stream.u8() self.sequence_id = stream.u16() if self.settings.get("pia.header_version") == 0: self.session_timer = stream.u16() self.rtt_timer = stream.u16() if self.settings.get( "pia.encryption_method") == EncryptionMethod.AES_GCM: self.nonce = stream.u64() self.signature = stream.read(16) payload = stream.read(stream.available()) if encrypted: payload = self.decrypt(payload) if payload is None: return False self.payload = payload return True
def handle_connection_request(self, station, data): logger.debug("Received connection request") stream = StreamIn(data, self.settings) stream.skip(1) connection_id = stream.u8() version = stream.u8() inverse = stream.bool() if version != self.version: logger.error( "Received connection request with wrong version number") self.send_denying_connection_response(station, 2) return local_station = self.session.local_station() local_info = local_station.connection_info.local if self.version >= 7: if stream.pid() != local_info.pid: logger.error("Received connection request with wrong pid") return if self.version >= 8: if stream.u32() != local_info.cid: logger.error("Received connection request with wrong cid") return inverse_id = stream.u8() if inverse_id != station.connection_id_out_temp: logger.error( "Received connection request with wrong inverse connection id: %i", inverse_id) return if self.version == 8: conn_info = stream.extract(StationConnectionInfo) else: location = stream.extract(StationLocation) conn_info = StationConnectionInfo(location) ack_id = stream.u32() if inverse: if station.connection_state != ConnectionState.WAIT_INVERSE_REQUEST: logger.error("Received unexpected inverse connection request") self.send_denying_connection_response(station, 1) return station.connection_id_in_temp = connection_id station.connection_info = conn_info self.send_ack(station, ack_id) self.send_connection_response(station) station.connection_state = ConnectionState.WAIT_RESPONSE else: if station.connection_state != ConnectionState.DISCONNECTED: logger.error("Received unexpected connection request") self.send_denying_connection_response(station, 1) return station.connection_id_out_temp = random.randint(2, 0xFF) station.connection_id_in_temp = connection_id station.connection_info = conn_info self.send_ack(station, ack_id) self.send_connection_request(station, connection_id) station.connection_state = ConnectionState.WAIT_INVERSE_RESPONSE