Exemplo n.º 1
0
    async def close_connection(self, stream: Stream):  # Close a connection to a client
        try:
            await stream.drain()
        except (ConnectionResetError, BrokenPipeError):
            pass

        try:
            stream.close()
            await stream.wait_closed()
        except (ConnectionResetError, BrokenPipeError):
            pass

        try:
            del self.cache.states[stream.remote]
        except KeyError:
            pass

        try:
            del self.cache.login[stream.remote]
        except KeyError:
            pass

        try:
            del self.playerio.cache[self.cache.uuid[stream.remote]]
        except KeyError:
            pass

        try:
            del self.cache.uuid[stream.remote]
        except KeyError:
            pass

        self.console.debug(f"Disconnected nicely from {stream.remote[0]}:{stream.remote[1]}.")

        return False, stream
Exemplo n.º 2
0
async def login_start(stream: Stream, packet: Packet) -> tuple:
    if share["conf"][
            "online_mode"]:  # Online mode is enabled, so we request encryption
        lc = login_cache[stream.remote] = {
            "username": packet.username,
            "verify": None
        }

        packet = login_packets.LoginEncryptionRequest(
            share["rsa"]["public"].public_bytes(
                encoding=serialization.Encoding.DER,
                format=serialization.PublicFormat.SubjectPublicKeyInfo))

        lc["verify"] = packet.verify_token

        stream.write(Buffer.pack_packet(packet))
        await stream.drain()
    else:  # No need for encryption since online mode is off, just send login success
        uuid_ = (
            uuid.uuid4()
        )  # This should be only generated if the player name isn't found in the world data, but no way to do that rn

        stream.write(
            Buffer.pack_packet(
                login_packets.LoginSuccess(uuid_, packet.username),
                share["comp_thresh"]))
        await stream.drain()

        states[stream.remote] = 3  # Update state to play

    return True, stream
Exemplo n.º 3
0
    async def send_packet(self, stream: Stream, packet: Packet, comp_thresh=None):
        self.console.debug(f"OUT: state:-1 | id:0x{packet.id:02X} | packet:{type(packet).__name__}")

        if comp_thresh is None:
            comp_thresh = self.comp_thresh

        stream.write(Buffer.pack_packet(packet, comp_thresh))
        await stream.drain()
Exemplo n.º 4
0
    async def handle_connection(self, reader, writer):  # Handle a connection from a client
        stream = Stream(reader, writer)
        self.console.debug(f"Connection received from {stream.remote[0]}:{stream.remote[1]}.")

        error_count = 0

        while True:
            try:
                stream = await self.handle_packet(stream)
            except StopHandling:
                break
            except (ConnectionResetError, BrokenPipeError):
                break
            except BaseException as e:
                error_count += 1

                self.console.error(self.console.f_traceback(e))

                if error_count > 1:
                    break

            if error_count > 0:
                error_count -= 0.5

        await self.close_connection(stream)
Exemplo n.º 5
0
async def handle_packet(stream: Stream):
    packet_length = 0

    # Basically an implementation of Buffer.unpack_varint()
    # except designed to read directly from a a StreamReader
    # and also to handle legacy server list ping packets
    for i in range(5):
        try:
            read = await asyncio.wait_for(stream.read(1), 5)
        except asyncio.TimeoutError:
            logger.debug("Closing due to timeout on read...")
            return False, stream

        if read == b"":
            logger.debug("Closing due to invalid read....")
            return False, stream

        if i == 0 and read == b"\xFE":
            logger.warn("Legacy ping attempted, legacy ping is not supported.")
            return False, stream

        b = struct.unpack("B", read)[0]
        packet_length |= (b & 0x7F) << 7 * i

        if not b & 0x80:
            break

    if packet_length & (1 << 31):
        packet_length -= 1 << 32

    buf = Buffer(await stream.read(packet_length))

    state = STATES.encode(states.get(stream.remote, 0))
    packet = buf.unpack_packet(state, PACKET_MAP)

    logger.debug(
        f"IN : state:{state:<11} | id:0x{packet.id:02X} | packet:{type(packet).__name__}"
    )

    for handler in pymine_api.packet.PACKET_HANDLERS[state][packet.id]:
        resp_value = await handler(stream, packet)

        try:
            continue_, stream = resp_value
        except (
                ValueError,
                TypeError,
        ):
            logger.warn(
                f"Invalid return from packet handler: {handler.__module__}.{handler.__qualname__}"
            )
            continue

        if not continue_:
            return False, stream

    return continue_, stream
Exemplo n.º 6
0
    async def close_connection(
            self, stream: Stream):  # Close a connection to a client
        await stream.drain()

        stream.close()
        await stream.wait_closed()

        try:
            del self.cache.states[stream.remote]
        except KeyError:
            pass

        try:
            del self.cache.login[stream.remote]
        except KeyError:
            pass

        self.logger.debug(
            f"Disconnected nicely from {stream.remote[0]}:{stream.remote[1]}.")

        return False, stream
Exemplo n.º 7
0
async def encrypted_login(stream: Stream, packet: Packet) -> tuple:
    shared_key, auth = await server_auth(packet, stream.remote,
                                         login_cache[stream.remote])

    del login_cache[stream.remote]  # No longer needed

    if not auth:  # If authentication failed, disconnect client
        stream.write(
            Buffer.pack_packet(
                login_packets.LoginDisconnect(
                    "Failed to authenticate your connection.")))
        await stream.drain()
        return False, stream

    # Generate a cipher for that client using the shared key from the client
    cipher = encryption.gen_aes_cipher(shared_key)

    # Replace stream with one which auto decrypts + encrypts data when reading/writing
    stream = EncryptedStream(stream, cipher)

    if share["comp_thresh"] > 0:  # Send set compression packet if needed
        stream.write(
            Buffer.pack_packet(LoginSetCompression(share["comp_thresh"])))
        await stream.drain()

    # Send LoginSuccess packet, tells client they've logged in succesfully
    stream.write(
        Buffer.pack_packet(login_packets.LoginSuccess(*auth),
                           share["comp_thresh"]))
    await stream.drain()

    return True, stream
Exemplo n.º 8
0
async def send_status(stream: Stream, packet: Packet) -> tuple:
    data = {
        "version": {
            "name": share["version"],
            "protocol": share["protocol"]
        },
        "players": {
            "max":
            share["conf"]["max_players"],
            "online":
            len(share["states"]),
            "sample": [
                {
                    "name": "Iapetus11",
                    "id": "cbcfa252-867d-4bda-a214-776c881cf370"
                },
                {
                    "name": "Sh_wayz",
                    "id": "cbcfa252-867d-4bda-a214-776c881cf370"
                },
                {
                    "name": "emeralddragonmc",
                    "id": "eb86dc19-c3f5-4aef-a50e-a4bf435a7528"
                },
            ],
        },
        "description": {
            "text": share["conf"]["motd"]
        },  # a Chat
    }

    if share["favicon"]:
        data["favicon"] = share["favicon"]

    stream.write(Buffer.pack_packet(status_packets.StatusStatusResponse(data)))
    await stream.drain()

    return True, stream
Exemplo n.º 9
0
    async def handle_connection(self, reader,
                                writer):  # Handle a connection from a client
        stream = Stream(reader, writer)
        self.logger.debug(
            f"Connection received from {stream.remote[0]}:{stream.remote[1]}.")

        while True:
            try:
                stream = await self.handle_packet(stream)
            except StopHandling:
                break
            except BaseException as e:
                self.logger.error(self.logger.f_traceback(e))

        await self.close_connection(stream)
Exemplo n.º 10
0
async def handle_con(reader, writer):  # Handle a connection from a client
    stream = Stream(reader, writer)
    logger.debug(
        f"Connection received from {stream.remote[0]}:{stream.remote[1]}.")

    continue_ = True

    while continue_:
        try:
            continue_, stream = await handle_packet(stream)
        except BaseException as e:
            logger.error(logger.f_traceback(e))
            break

    await close_con(stream)
Exemplo n.º 11
0
    async def handle_packet(
        self, stream: Stream
    ):  # Handle / respond to packets, this is called in a loop
        packet_length = 0

        # Basically an implementation of Buffer.unpack_varint()
        # except designed to read directly from a a StreamReader
        # and also to handle legacy server list ping packets
        for i in range(3):
            try:
                read = await asyncio.wait_for(stream.read(1), 30)
            except asyncio.TimeoutError:
                self.console.debug("Closing due to timeout on read...")
                raise StopHandling

            if read == b"":
                self.console.debug("Closing due to invalid read....")
                raise StopHandling

            if i == 0 and read == b"\xFE":
                self.console.warn("Legacy ping attempted, legacy ping is not supported.")
                raise StopHandling

            b = struct.unpack(">B", read)[0]
            packet_length |= (b & 0x7F) << 7 * i

            if not b & 0x80:
                break

        if packet_length & (1 << 31):
            packet_length -= 1 << 32

        buf = Buffer(await stream.read(packet_length))

        state = self.cache.states.get(stream.remote, 0)

        try:
            packet = buf.unpack_packet(state, PACKET_MAP)
        except InvalidPacketID:
            self.console.warn("Invalid packet ID received.")
            return stream

        self.console.debug(
            f"IN : state: {state} | id:0x{packet.id:02X} | packet:{type(packet).__name__}"
        )

        if self.api.register._on_packet[state].get(packet.id) is None:
            self.console.warn(
                f"No packet handler found for packet: 0x{packet.id:02X} {type(packet).__name__}"
            )
            return stream

        for handler in self.api.register._on_packet[state][packet.id].values():
            try:
                res = await handler(stream, packet)

                if isinstance(res, Stream):
                    stream = res
            except StopHandling:
                raise
            except BaseException as e:
                self.console.error(
                    f"Error occurred in {handler.__module__}.{handler.__qualname__}: {self.console.f_traceback(e)}"
                )

        return stream
Exemplo n.º 12
0
async def send_pong(stream: Stream, packet: Packet) -> tuple:
    stream.write(Buffer.pack_packet(packet))
    await stream.drain()

    return False, stream