Ejemplo n.º 1
0
 def test_invalid_nested(self) -> None:
     """List and dictionaries are recursively searched."""
     with self.assertRaises(SynapseError):
         event_from_pdu_json(
             {
                 "type": EventTypes.Message,
                 "content": {"foo": [{"bar": 2**56}]},
                 "room_id": "!room:test",
                 "sender": "@user:test",
                 "depth": 1,
                 "prev_events": [],
                 "auth_events": [],
                 "origin_server_ts": 1234,
             },
             RoomVersions.V6,
         )
Ejemplo n.º 2
0
 def on_invite_request(self, origin, content):
     pdu = event_from_pdu_json(content)
     origin_host, _ = parse_server_name(origin)
     yield self.check_server_matches_acl(origin_host, pdu.room_id)
     ret_pdu = yield self.handler.on_invite_request(origin, pdu)
     time_now = self._clock.time_msec()
     defer.returnValue((200, {"event": ret_pdu.get_pdu_json(time_now)}))
Ejemplo n.º 3
0
    def send_invite(self, destination, room_id, event_id, pdu):
        time_now = self._clock.time_msec()
        try:
            code, content = yield self.transport_layer.send_invite(
                destination=destination,
                room_id=room_id,
                event_id=event_id,
                content=pdu.get_pdu_json(time_now),
            )
        except HttpResponseException as e:
            if e.code == 403:
                raise e.to_synapse_error()
            raise

        pdu_dict = content["event"]

        logger.debug("Got response to send_invite: %s", pdu_dict)

        pdu = event_from_pdu_json(pdu_dict)

        # Check signatures are correct.
        pdu = yield self._check_sigs_and_hash(pdu)

        # FIXME: We should handle signature failures more gracefully.

        defer.returnValue(pdu)
Ejemplo n.º 4
0
    def get_event_auth(self, destination, room_id, event_id):
        res = yield self.transport_layer.get_event_auth(
            destination,
            room_id,
            event_id,
        )

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)

        auth_chain = [
            event_from_pdu_json(p, format_ver, outlier=True)
            for p in res["auth_chain"]
        ]

        signed_auth = yield self._check_sigs_and_hash_and_fetch(
            destination,
            auth_chain,
            outlier=True,
            room_version=room_version,
        )

        signed_auth.sort(key=lambda e: e.depth)

        defer.returnValue(signed_auth)
Ejemplo n.º 5
0
    def test_prune_gap(self):
        """Test that we drop extremities after a gap when we see an event from
        the same domain.
        """

        # Fudge a second event which points to an event we don't have. This is a
        # state event so that the state changes (otherwise we won't prune the
        # extremity as they'll have the same state group).
        remote_event_2 = event_from_pdu_json(
            {
                "type": EventTypes.Member,
                "state_key": "@user:other",
                "content": {"membership": Membership.JOIN},
                "room_id": self.room_id,
                "sender": "@user:other",
                "depth": 50,
                "prev_events": ["$some_unknown_message"],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        state_before_gap = self.get_success(self.state.get_current_state(self.room_id))

        self.persist_event(remote_event_2, state=state_before_gap.values())

        # Check the new extremity is just the new remote event.
        self.assert_extremities([remote_event_2.event_id])
Ejemplo n.º 6
0
    async def backfill(
            self, dest: str, room_id: str, limit: int,
            extremities: Iterable[str]) -> Optional[List[EventBase]]:
        """Requests some more historic PDUs for the given room from the
        given destination server.

        Args:
            dest (str): The remote homeserver to ask.
            room_id (str): The room_id to backfill.
            limit (int): The maximum number of events to return.
            extremities (list): our current backwards extremities, to backfill from
        """
        logger.debug("backfill extrem=%s", extremities)

        # If there are no extremities then we've (probably) reached the start.
        if not extremities:
            return None

        transaction_data = await self.transport_layer.backfill(
            dest, room_id, extremities, limit)

        logger.debug("backfill transaction_data=%r", transaction_data)

        room_version = await self.store.get_room_version(room_id)

        pdus = [
            event_from_pdu_json(p, room_version, outlier=False)
            for p in transaction_data["pdus"]
        ]

        # Check signatures and hash of pdus, removing any from the list that fail checks
        pdus[:] = await self._check_sigs_and_hash_and_fetch(
            dest, pdus, outlier=True, room_version=room_version)

        return pdus
Ejemplo n.º 7
0
    def test_prune_gap_if_old(self):
        """Test that we drop extremities after a gap when the previous extremity
        is "old"
        """

        # Advance the clock for many days to make the old extremity "old". We
        # also set the depth to "lots".
        self.reactor.advance(7 * 24 * 60 * 60)

        # Fudge a second event which points to an event we don't have. This is a
        # state event so that the state changes (otherwise we won't prune the
        # extremity as they'll have the same state group).
        remote_event_2 = event_from_pdu_json(
            {
                "type": EventTypes.Member,
                "state_key": "@user:other2",
                "content": {"membership": Membership.JOIN},
                "room_id": self.room_id,
                "sender": "@user:other2",
                "depth": 10000,
                "prev_events": ["$some_unknown_message"],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        state_before_gap = self.get_success(self.state.get_current_state(self.room_id))

        self.persist_event(remote_event_2, state=state_before_gap.values())

        # Check the new extremity is just the new remote event.
        self.assert_extremities([remote_event_2.event_id])
Ejemplo n.º 8
0
    def test_do_not_prune_gap_if_not_dummy(self):
        """Test that we do not drop extremities after a gap when the previous extremity
        is not a dummy event.
        """

        body = self.helper.send(self.room_id, body="test", tok=self.token)
        local_message_event_id = body["event_id"]
        self.assert_extremities([local_message_event_id])

        # Fudge a second event which points to an event we don't have. This is a
        # state event so that the state changes (otherwise we won't prune the
        # extremity as they'll have the same state group).
        remote_event_2 = event_from_pdu_json(
            {
                "type": EventTypes.Member,
                "state_key": "@user:other2",
                "content": {"membership": Membership.JOIN},
                "room_id": self.room_id,
                "sender": "@user:other2",
                "depth": 10000,
                "prev_events": ["$some_unknown_message"],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        state_before_gap = self.get_success(self.state.get_current_state(self.room_id))

        self.persist_event(remote_event_2, state=state_before_gap.values())

        # Check the new extremity is just the new remote event.
        self.assert_extremities([local_message_event_id, remote_event_2.event_id])
Ejemplo n.º 9
0
    def send_invite(self, destination, room_id, event_id, pdu):
        time_now = self._clock.time_msec()
        try:
            code, content = yield self.transport_layer.send_invite(
                destination=destination,
                room_id=room_id,
                event_id=event_id,
                content=pdu.get_pdu_json(time_now),
            )
        except HttpResponseException as e:
            if e.code == 403:
                raise e.to_synapse_error()
            raise

        pdu_dict = content["event"]

        logger.debug("Got response to send_invite: %s", pdu_dict)

        pdu = event_from_pdu_json(pdu_dict)

        # Check signatures are correct.
        pdu = yield self._check_sigs_and_hash(pdu)

        # FIXME: We should handle signature failures more gracefully.

        defer.returnValue(pdu)
Ejemplo n.º 10
0
    def prepare(self, reactor, clock, homeserver):
        self.state = self.hs.get_state_handler()
        self.persistence = self.hs.get_storage().persistence
        self.store = self.hs.get_datastore()

        self.register_user("user", "pass")
        self.token = self.login("user", "pass")

        self.room_id = self.helper.create_room_as(
            "user", room_version=RoomVersions.V6.identifier, tok=self.token)

        body = self.helper.send(self.room_id, body="Test", tok=self.token)
        local_message_event_id = body["event_id"]

        # Fudge a remote event and persist it. This will be the extremity before
        # the gap.
        self.remote_event_1 = event_from_pdu_json(
            {
                "type": EventTypes.Message,
                "state_key": "@user:other",
                "content": {},
                "room_id": self.room_id,
                "sender": "@user:other",
                "depth": 5,
                "prev_events": [local_message_event_id],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        self.persist_event(self.remote_event_1)

        # Check that the current extremities is the remote event.
        self.assert_extremities([self.remote_event_1.event_id])
Ejemplo n.º 11
0
    async def on_send_join_request(self, origin, content, room_id):
        logger.debug("on_send_join_request: content: %s", content)

        room_version = await self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)
        pdu = event_from_pdu_json(content, format_ver)

        origin_host, _ = parse_server_name(origin)
        await self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_join_request: pdu sigs: %s", pdu.signatures)

        pdu = await self._check_sigs_and_hash(room_version, pdu)

        res_pdus = await self.handler.on_send_join_request(origin, pdu)
        time_now = self._clock.time_msec()
        return (
            200,
            {
                "state": [p.get_pdu_json(time_now) for p in res_pdus["state"]],
                "auth_chain": [
                    p.get_pdu_json(time_now) for p in res_pdus["auth_chain"]
                ],
            },
        )
Ejemplo n.º 12
0
    def query_auth(self, destination, room_id, event_id, local_auth):
        """
        Params:
            destination (str)
            event_it (str)
            local_auth (list)
        """
        time_now = self._clock.time_msec()

        send_content = {
            "auth_chain": [e.get_pdu_json(time_now) for e in local_auth],
        }

        code, content = yield self.transport_layer.send_query_auth(
            destination=destination,
            room_id=room_id,
            event_id=event_id,
            content=send_content,
        )

        auth_chain = [event_from_pdu_json(e) for e in content["auth_chain"]]

        signed_auth = yield self._check_sigs_and_hash_and_fetch(destination,
                                                                auth_chain,
                                                                outlier=True)

        signed_auth.sort(key=lambda e: e.depth)

        ret = {
            "auth_chain": signed_auth,
            "rejects": content.get("rejects", []),
            "missing": content.get("missing", []),
        }

        defer.returnValue(ret)
Ejemplo n.º 13
0
 def on_invite_request(self, origin, content):
     pdu = event_from_pdu_json(content)
     origin_host, _ = parse_server_name(origin)
     yield self.check_server_matches_acl(origin_host, pdu.room_id)
     ret_pdu = yield self.handler.on_invite_request(origin, pdu)
     time_now = self._clock.time_msec()
     defer.returnValue((200, {"event": ret_pdu.get_pdu_json(time_now)}))
Ejemplo n.º 14
0
 async def get_event_auth(destination: str, room_id: str,
                          event_id: str) -> List[EventBase]:
     return [
         event_from_pdu_json(ae.get_pdu_json(),
                             room_version=room_version,
                             outlier=True) for ae in auth_events
     ]
Ejemplo n.º 15
0
    def on_query_auth_request(self, origin, content, room_id, event_id):
        """
        Content is a dict with keys::
            auth_chain (list): A list of events that give the auth chain.
            missing (list): A list of event_ids indicating what the other
              side (`origin`) think we're missing.
            rejects (dict): A mapping from event_id to a 2-tuple of reason
              string and a proof (or None) of why the event was rejected.
              The keys of this dict give the list of events the `origin` has
              rejected.

        Args:
            origin (str)
            content (dict)
            event_id (str)

        Returns:
            Deferred: Results in `dict` with the same format as `content`
        """
        with (yield self._server_linearizer.queue((origin, room_id))):
            origin_host, _ = parse_server_name(origin)
            yield self.check_server_matches_acl(origin_host, room_id)

            room_version = yield self.store.get_room_version(room_id)
            format_ver = room_version_to_event_format(room_version)

            auth_chain = [
                event_from_pdu_json(e, format_ver)
                for e in content["auth_chain"]
            ]

            signed_auth = yield self._check_sigs_and_hash_and_fetch(
                origin, auth_chain, outlier=True, room_version=room_version,
            )

            ret = yield self.handler.on_query_auth(
                origin,
                event_id,
                room_id,
                signed_auth,
                content.get("rejects", []),
                content.get("missing", []),
            )

            time_now = self._clock.time_msec()
            send_content = {
                "auth_chain": [
                    e.get_pdu_json(time_now)
                    for e in ret["auth_chain"]
                ],
                "rejects": ret.get("rejects", []),
                "missing": ret.get("missing", []),
            }

        defer.returnValue(
            (200, send_content)
        )
Ejemplo n.º 16
0
    def on_query_auth_request(self, origin, content, room_id, event_id):
        """
        Content is a dict with keys::
            auth_chain (list): A list of events that give the auth chain.
            missing (list): A list of event_ids indicating what the other
              side (`origin`) think we're missing.
            rejects (dict): A mapping from event_id to a 2-tuple of reason
              string and a proof (or None) of why the event was rejected.
              The keys of this dict give the list of events the `origin` has
              rejected.

        Args:
            origin (str)
            content (dict)
            event_id (str)

        Returns:
            Deferred: Results in `dict` with the same format as `content`
        """
        with (yield self._server_linearizer.queue((origin, room_id))):
            origin_host, _ = parse_server_name(origin)
            yield self.check_server_matches_acl(origin_host, room_id)

            room_version = yield self.store.get_room_version(room_id)
            format_ver = room_version_to_event_format(room_version)

            auth_chain = [
                event_from_pdu_json(e, format_ver)
                for e in content["auth_chain"]
            ]

            signed_auth = yield self._check_sigs_and_hash_and_fetch(
                origin, auth_chain, outlier=True, room_version=room_version,
            )

            ret = yield self.handler.on_query_auth(
                origin,
                event_id,
                room_id,
                signed_auth,
                content.get("rejects", []),
                content.get("missing", []),
            )

            time_now = self._clock.time_msec()
            send_content = {
                "auth_chain": [
                    e.get_pdu_json(time_now)
                    for e in ret["auth_chain"]
                ],
                "rejects": ret.get("rejects", []),
                "missing": ret.get("missing", []),
            }

        defer.returnValue(
            (200, send_content)
        )
Ejemplo n.º 17
0
    def on_invite_request(self, origin, content, room_version):
        format_ver = room_version_to_event_format(room_version)

        pdu = event_from_pdu_json(content, format_ver)
        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)
        ret_pdu = yield self.handler.on_invite_request(origin, pdu)
        time_now = self._clock.time_msec()
        defer.returnValue({"event": ret_pdu.get_pdu_json(time_now)})
Ejemplo n.º 18
0
    async def get_missing_events(
        self,
        destination: str,
        room_id: str,
        earliest_events_ids: Sequence[str],
        latest_events: Iterable[EventBase],
        limit: int,
        min_depth: int,
        timeout: int,
    ) -> List[EventBase]:
        """Tries to fetch events we are missing. This is called when we receive
        an event without having received all of its ancestors.

        Args:
            destination
            room_id
            earliest_events_ids: List of event ids. Effectively the
                events we expected to receive, but haven't. `get_missing_events`
                should only return events that didn't happen before these.
            latest_events: List of events we have received that we don't
                have all previous events for.
            limit: Maximum number of events to return.
            min_depth: Minimum depth of events to return.
            timeout: Max time to wait in ms
        """
        try:
            content = await self.transport_layer.get_missing_events(
                destination=destination,
                room_id=room_id,
                earliest_events=earliest_events_ids,
                latest_events=[e.event_id for e in latest_events],
                limit=limit,
                min_depth=min_depth,
                timeout=timeout,
            )

            room_version = await self.store.get_room_version(room_id)

            events = [
                event_from_pdu_json(e, room_version)
                for e in content.get("events", [])
            ]

            signed_events = await self._check_sigs_and_hash_and_fetch(
                destination,
                events,
                outlier=False,
                room_version=room_version.identifier)
        except HttpResponseException as e:
            if not e.code == 400:
                raise

            # We are probably hitting an old server that doesn't support
            # get_missing_events
            signed_events = []

        return signed_events
Ejemplo n.º 19
0
    def on_send_leave_request(self, origin, content):
        logger.debug("on_send_leave_request: content: %s", content)
        pdu = event_from_pdu_json(content)

        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
        yield self.handler.on_send_leave_request(origin, pdu)
        defer.returnValue((200, {}))
Ejemplo n.º 20
0
    def on_send_leave_request(self, origin, content):
        logger.debug("on_send_leave_request: content: %s", content)
        pdu = event_from_pdu_json(content)

        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
        yield self.handler.on_send_leave_request(origin, pdu)
        defer.returnValue((200, {}))
Ejemplo n.º 21
0
    def get_missing_events(
        self,
        destination,
        room_id,
        earliest_events_ids,
        latest_events,
        limit,
        min_depth,
        timeout,
    ):
        """Tries to fetch events we are missing. This is called when we receive
        an event without having received all of its ancestors.

        Args:
            destination (str)
            room_id (str)
            earliest_events_ids (list): List of event ids. Effectively the
                events we expected to receive, but haven't. `get_missing_events`
                should only return events that didn't happen before these.
            latest_events (list): List of events we have received that we don't
                have all previous events for.
            limit (int): Maximum number of events to return.
            min_depth (int): Minimum depth of events tor return.
            timeout (int): Max time to wait in ms
        """
        try:
            content = yield self.transport_layer.get_missing_events(
                destination=destination,
                room_id=room_id,
                earliest_events=earliest_events_ids,
                latest_events=[e.event_id for e in latest_events],
                limit=limit,
                min_depth=min_depth,
                timeout=timeout,
            )

            room_version = yield self.store.get_room_version(room_id)
            format_ver = room_version_to_event_format(room_version)

            events = [
                event_from_pdu_json(e, format_ver)
                for e in content.get("events", [])
            ]

            signed_events = yield self._check_sigs_and_hash_and_fetch(
                destination, events, outlier=False, room_version=room_version)
        except HttpResponseException as e:
            if not e.code == 400:
                raise

            # We are probably hitting an old server that doesn't support
            # get_missing_events
            signed_events = []

        defer.returnValue(signed_events)
Ejemplo n.º 22
0
    def test_rejected_state_event_state(self):
        """
        Check that we store the state group correctly for rejected state events.

        Regression test for #6289.
        """
        OTHER_SERVER = "otherserver"
        OTHER_USER = "******" + OTHER_SERVER

        # create the room
        user_id = self.register_user("kermit", "test")
        tok = self.login("kermit", "test")
        room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
        room_version = self.get_success(self.store.get_room_version(room_id))

        # pretend that another server has joined
        join_event = self._build_and_send_join_event(OTHER_SERVER, OTHER_USER,
                                                     room_id)

        # check the state group
        sg = self.successResultOf(
            self.store._get_state_group_for_event(join_event.event_id))

        # build and send an event which will be rejected
        ev = event_from_pdu_json(
            {
                "type": "org.matrix.test",
                "state_key": "test_key",
                "content": {},
                "room_id": room_id,
                "sender": "@yetanotheruser:"******"depth": join_event["depth"] + 1,
                "prev_events": [join_event.event_id],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            room_version,
        )

        with LoggingContext("send_rejected"):
            d = run_in_background(
                self.hs.get_federation_event_handler().on_receive_pdu,
                OTHER_SERVER, ev)
        self.get_success(d)

        # that should have been rejected
        e = self.get_success(
            self.store.get_event(ev.event_id, allow_rejected=True))
        self.assertIsNotNone(e.rejected_reason)

        # ... and the state group should be the same as before
        sg2 = self.successResultOf(
            self.store._get_state_group_for_event(ev.event_id))

        self.assertEqual(sg, sg2)
Ejemplo n.º 23
0
 def on_send_join_request(self, origin, content):
     logger.debug("on_send_join_request: content: %s", content)
     pdu = event_from_pdu_json(content)
     logger.debug("on_send_join_request: pdu sigs: %s", pdu.signatures)
     res_pdus = yield self.handler.on_send_join_request(origin, pdu)
     time_now = self._clock.time_msec()
     defer.returnValue((200, {
         "state": [p.get_pdu_json(time_now) for p in res_pdus["state"]],
         "auth_chain":
         [p.get_pdu_json(time_now) for p in res_pdus["auth_chain"]],
     }))
Ejemplo n.º 24
0
 def on_send_join_request(self, origin, content):
     logger.debug("on_send_join_request: content: %s", content)
     pdu = event_from_pdu_json(content)
     logger.debug("on_send_join_request: pdu sigs: %s", pdu.signatures)
     res_pdus = yield self.handler.on_send_join_request(origin, pdu)
     time_now = self._clock.time_msec()
     defer.returnValue((200, {
         "state": [p.get_pdu_json(time_now) for p in res_pdus["state"]],
         "auth_chain": [
             p.get_pdu_json(time_now) for p in res_pdus["auth_chain"]
         ],
     }))
Ejemplo n.º 25
0
    def on_send_leave_request(self, origin, content, room_id):
        logger.debug("on_send_leave_request: content: %s", content)

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)
        pdu = event_from_pdu_json(content, format_ver)

        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
        yield self.handler.on_send_leave_request(origin, pdu)
        defer.returnValue((200, {}))
Ejemplo n.º 26
0
    def on_send_leave_request(self, origin, content, room_id):
        logger.debug("on_send_leave_request: content: %s", content)

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)
        pdu = event_from_pdu_json(content, format_ver)

        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
        yield self.handler.on_send_leave_request(origin, pdu)
        defer.returnValue((200, {}))
Ejemplo n.º 27
0
    def test_room_remote_user_cache_invalidated(self):
        """Test that if the server leaves a room the `get_users_in_room` cache
        is invalidated for remote users.
        """

        # Set up a room with a local and remote user in it.
        user_id = self.register_user("user", "pass")
        token = self.login("user", "pass")

        room_id = self.helper.create_room_as(
            "user", room_version=RoomVersions.V6.identifier, tok=token)

        body = self.helper.send(room_id, body="Test", tok=token)
        local_message_event_id = body["event_id"]

        # Fudge a join event for a remote user.
        remote_user = "******"
        remote_event_1 = event_from_pdu_json(
            {
                "type": EventTypes.Member,
                "state_key": remote_user,
                "content": {
                    "membership": Membership.JOIN
                },
                "room_id": room_id,
                "sender": remote_user,
                "depth": 5,
                "prev_events": [local_message_event_id],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        context = self.get_success(
            self.state.compute_event_context(remote_event_1))
        self.get_success(
            self._persistence.persist_event(remote_event_1, context))

        # Call `get_users_in_room` to add the remote user to the cache
        users = self.get_success(self.store.get_users_in_room(room_id))
        self.assertEqual(set(users), {user_id, remote_user})

        # Now we have the local server leave the room, and check that calling
        # `get_user_in_room` for the remote user no longer includes the room.
        self.helper.leave(room_id, user_id, tok=token)

        users = self.get_success(self.store.get_users_in_room(room_id))
        self.assertEqual(users, [])
Ejemplo n.º 28
0
 def test_invalid_numbers(self) -> None:
     """Invalid values for an integer should be rejected, all floats should be rejected."""
     for value in [
         -(2**53),
         2**53,
         1.0,
         float("inf"),
         float("-inf"),
         float("nan"),
     ]:
         with self.assertRaises(SynapseError):
             event_from_pdu_json(
                 {
                     "type": EventTypes.Message,
                     "content": {"foo": value},
                     "room_id": "!room:test",
                     "sender": "@user:test",
                     "depth": 1,
                     "prev_events": [],
                     "auth_events": [],
                     "origin_server_ts": 1234,
                 },
                 RoomVersions.V6,
             )
Ejemplo n.º 29
0
    async def on_send_leave_request(self, origin, content, room_id):
        logger.debug("on_send_leave_request: content: %s", content)

        room_version = await self.store.get_room_version(room_id)
        pdu = event_from_pdu_json(content, room_version)

        origin_host, _ = parse_server_name(origin)
        await self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)

        pdu = await self._check_sigs_and_hash(room_version.identifier, pdu)

        await self.handler.on_send_leave_request(origin, pdu)
        return {}
Ejemplo n.º 30
0
    def get_missing_events(self, destination, room_id, earliest_events_ids,
                           latest_events, limit, min_depth, timeout):
        """Tries to fetch events we are missing. This is called when we receive
        an event without having received all of its ancestors.

        Args:
            destination (str)
            room_id (str)
            earliest_events_ids (list): List of event ids. Effectively the
                events we expected to receive, but haven't. `get_missing_events`
                should only return events that didn't happen before these.
            latest_events (list): List of events we have received that we don't
                have all previous events for.
            limit (int): Maximum number of events to return.
            min_depth (int): Minimum depth of events tor return.
            timeout (int): Max time to wait in ms
        """
        try:
            content = yield self.transport_layer.get_missing_events(
                destination=destination,
                room_id=room_id,
                earliest_events=earliest_events_ids,
                latest_events=[e.event_id for e in latest_events],
                limit=limit,
                min_depth=min_depth,
                timeout=timeout,
            )

            room_version = yield self.store.get_room_version(room_id)
            format_ver = room_version_to_event_format(room_version)

            events = [
                event_from_pdu_json(e, format_ver)
                for e in content.get("events", [])
            ]

            signed_events = yield self._check_sigs_and_hash_and_fetch(
                destination, events, outlier=False, room_version=room_version,
            )
        except HttpResponseException as e:
            if not e.code == 400:
                raise

            # We are probably hitting an old server that doesn't support
            # get_missing_events
            signed_events = []

        defer.returnValue(signed_events)
Ejemplo n.º 31
0
 def test_strip_unauthorized_unsigned_values(self):
     event1 = {
         "sender": "@baduser:test.serv",
         "state_key": "@baduser:test.serv",
         "event_id": "$event1:test.serv",
         "depth": 1000,
         "origin_server_ts": 1,
         "type": "m.room.member",
         "origin": "test.servx",
         "content": {"membership": "join"},
         "auth_events": [],
         "unsigned": {"malicious garbage": "hackz", "more warez": "more hackz"},
     }
     filtered_event = event_from_pdu_json(event1, RoomVersions.V1)
     # Make sure unauthorized fields are stripped from unsigned
     self.assertNotIn("more warez", filtered_event.unsigned)
Ejemplo n.º 32
0
    async def on_send_leave_request(self, origin: str, content: JsonDict) -> dict:
        logger.debug("on_send_leave_request: content: %s", content)

        assert_params_in_dict(content, ["room_id"])
        room_version = await self.store.get_room_version(content["room_id"])
        pdu = event_from_pdu_json(content, room_version)

        origin_host, _ = parse_server_name(origin)
        await self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)

        pdu = await self._check_sigs_and_hash(room_version, pdu)

        await self.handler.on_send_leave_request(origin, pdu)
        return {}
Ejemplo n.º 33
0
    def on_invite_request(self, origin, content, room_version):
        if room_version not in KNOWN_ROOM_VERSIONS:
            raise SynapseError(
                400,
                "Homeserver does not support this room version",
                Codes.UNSUPPORTED_ROOM_VERSION,
            )

        format_ver = room_version_to_event_format(room_version)

        pdu = event_from_pdu_json(content, format_ver)
        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)
        ret_pdu = yield self.handler.on_invite_request(origin, pdu)
        time_now = self._clock.time_msec()
        defer.returnValue({"event": ret_pdu.get_pdu_json(time_now)})
Ejemplo n.º 34
0
    def on_invite_request(self, origin, content, room_version):
        if room_version not in KNOWN_ROOM_VERSIONS:
            raise SynapseError(
                400,
                "Homeserver does not support this room version",
                Codes.UNSUPPORTED_ROOM_VERSION,
            )

        format_ver = room_version_to_event_format(room_version)

        pdu = event_from_pdu_json(content, format_ver)
        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)
        ret_pdu = yield self.handler.on_invite_request(origin, pdu)
        time_now = self._clock.time_msec()
        defer.returnValue({"event": ret_pdu.get_pdu_json(time_now)})
Ejemplo n.º 35
0
    def get_event_auth(self, destination, room_id, event_id):
        res = yield self.transport_layer.get_event_auth(
            destination, room_id, event_id,
        )

        auth_chain = [
            event_from_pdu_json(p, outlier=True)
            for p in res["auth_chain"]
        ]

        signed_auth = yield self._check_sigs_and_hash_and_fetch(
            destination, auth_chain, outlier=True
        )

        signed_auth.sort(key=lambda e: e.depth)

        defer.returnValue(signed_auth)
Ejemplo n.º 36
0
    def test_valid_json(self) -> None:
        """Valid JSON should be turned into an event."""
        ev = event_from_pdu_json(
            {
                "type": EventTypes.Message,
                "content": {"bool": True, "null": None, "int": 1, "str": "foobar"},
                "room_id": "!room:test",
                "sender": "@user:test",
                "depth": 1,
                "prev_events": [],
                "auth_events": [],
                "origin_server_ts": 1234,
            },
            RoomVersions.V6,
        )

        self.assertIsInstance(ev, EventBase)
Ejemplo n.º 37
0
 def create_invite():
     room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
     room_version = self.get_success(self.store.get_room_version(room_id))
     return event_from_pdu_json(
         {
             "type": EventTypes.Member,
             "content": {"membership": "invite"},
             "room_id": room_id,
             "sender": other_user,
             "state_key": "@user:test",
             "depth": 32,
             "prev_events": [],
             "auth_events": [],
             "origin_server_ts": self.clock.time_msec(),
         },
         room_version,
     )
Ejemplo n.º 38
0
    async def get_event_auth(self, destination, room_id, event_id):
        res = await self.transport_layer.get_event_auth(
            destination, room_id, event_id)

        room_version = await self.store.get_room_version(room_id)

        auth_chain = [
            event_from_pdu_json(p, room_version, outlier=True)
            for p in res["auth_chain"]
        ]

        signed_auth = await self._check_sigs_and_hash_and_fetch(
            destination, auth_chain, outlier=True, room_version=room_version)

        signed_auth.sort(key=lambda e: e.depth)

        return signed_auth
Ejemplo n.º 39
0
    def test_prune_gap_if_dummy_local(self):
        """Test that we don't drop extremities after a gap when the previous
        extremity is a local dummy event and points to local events.
        """

        body = self.helper.send(self.room_id, body="Test", tok=self.token)

        body = self.helper.send_event(self.room_id,
                                      type=EventTypes.Dummy,
                                      content={},
                                      tok=self.token)
        local_message_event_id = body["event_id"]
        self.assert_extremities([local_message_event_id])

        # Advance the clock for many days to make the old extremity "old". We
        # also set the depth to "lots".
        self.reactor.advance(7 * 24 * 60 * 60)

        # Fudge a second event which points to an event we don't have. This is a
        # state event so that the state changes (otherwise we won't prune the
        # extremity as they'll have the same state group).
        remote_event_2 = event_from_pdu_json(
            {
                "type": EventTypes.Member,
                "state_key": "@user:other2",
                "content": {
                    "membership": Membership.JOIN
                },
                "room_id": self.room_id,
                "sender": "@user:other2",
                "depth": 10000,
                "prev_events": ["$some_unknown_message"],
                "auth_events": [],
                "origin_server_ts": self.clock.time_msec(),
            },
            RoomVersions.V6,
        )

        state_before_gap = self.get_success(
            self._state_storage_controller.get_current_state_ids(self.room_id))

        self.persist_event(remote_event_2, state=state_before_gap)

        # Check the new extremity is just the new remote event.
        self.assert_extremities(
            [remote_event_2.event_id, local_message_event_id])
Ejemplo n.º 40
0
    def on_send_join_request(self, origin, content, room_id):
        logger.debug("on_send_join_request: content: %s", content)

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)
        pdu = event_from_pdu_json(content, format_ver)

        origin_host, _ = parse_server_name(origin)
        yield self.check_server_matches_acl(origin_host, pdu.room_id)

        logger.debug("on_send_join_request: pdu sigs: %s", pdu.signatures)
        res_pdus = yield self.handler.on_send_join_request(origin, pdu)
        time_now = self._clock.time_msec()
        defer.returnValue((200, {
            "state": [p.get_pdu_json(time_now) for p in res_pdus["state"]],
            "auth_chain": [
                p.get_pdu_json(time_now) for p in res_pdus["auth_chain"]
            ],
        }))
Ejemplo n.º 41
0
    def send_invite(self, destination, room_id, event_id, pdu):
        room_version = yield self.store.get_room_version(room_id)

        content = yield self._do_send_invite(destination, pdu, room_version)

        pdu_dict = content["event"]

        logger.debug("Got response to send_invite: %s", pdu_dict)

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)

        pdu = event_from_pdu_json(pdu_dict, format_ver)

        # Check signatures are correct.
        pdu = yield self._check_sigs_and_hash(room_version, pdu)

        # FIXME: We should handle signature failures more gracefully.

        defer.returnValue(pdu)
Ejemplo n.º 42
0
    def query_auth(self, destination, room_id, event_id, local_auth):
        """
        Params:
            destination (str)
            event_it (str)
            local_auth (list)
        """
        time_now = self._clock.time_msec()

        send_content = {
            "auth_chain": [e.get_pdu_json(time_now) for e in local_auth],
        }

        code, content = yield self.transport_layer.send_query_auth(
            destination=destination,
            room_id=room_id,
            event_id=event_id,
            content=send_content,
        )

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)

        auth_chain = [
            event_from_pdu_json(e, format_ver)
            for e in content["auth_chain"]
        ]

        signed_auth = yield self._check_sigs_and_hash_and_fetch(
            destination, auth_chain, outlier=True, room_version=room_version,
        )

        signed_auth.sort(key=lambda e: e.depth)

        ret = {
            "auth_chain": signed_auth,
            "rejects": content.get("rejects", []),
            "missing": content.get("missing", []),
        }

        defer.returnValue(ret)
Ejemplo n.º 43
0
    def backfill(self, dest, room_id, limit, extremities):
        """Requests some more historic PDUs for the given context from the
        given destination server.

        Args:
            dest (str): The remote home server to ask.
            room_id (str): The room_id to backfill.
            limit (int): The maximum number of PDUs to return.
            extremities (list): List of PDU id and origins of the first pdus
                we have seen from the context

        Returns:
            Deferred: Results in the received PDUs.
        """
        logger.debug("backfill extrem=%s", extremities)

        # If there are no extremeties then we've (probably) reached the start.
        if not extremities:
            return

        transaction_data = yield self.transport_layer.backfill(
            dest, room_id, extremities, limit)

        logger.debug("backfill transaction_data=%s", repr(transaction_data))

        room_version = yield self.store.get_room_version(room_id)
        format_ver = room_version_to_event_format(room_version)

        pdus = [
            event_from_pdu_json(p, format_ver, outlier=False)
            for p in transaction_data["pdus"]
        ]

        # FIXME: We should handle signature failures more gracefully.
        pdus[:] = yield logcontext.make_deferred_yieldable(defer.gatherResults(
            self._check_sigs_and_hashes(room_version, pdus),
            consumeErrors=True,
        ).addErrback(unwrapFirstError))

        defer.returnValue(pdus)
Ejemplo n.º 44
0
    def _handle_incoming_transaction(self, transaction, request_time):
        """ Process an incoming transaction and return the HTTP response

        Args:
            transaction (Transaction): incoming transaction
            request_time (int): timestamp that the HTTP request arrived at

        Returns:
            Deferred[(int, object)]: http response code and body
        """
        response = yield self.transaction_actions.have_responded(transaction)

        if response:
            logger.debug(
                "[%s] We've already responded to this request",
                transaction.transaction_id
            )
            defer.returnValue(response)
            return

        logger.debug("[%s] Transaction is new", transaction.transaction_id)

        received_pdus_counter.inc_by(len(transaction.pdus))

        pdus_by_room = {}

        for p in transaction.pdus:
            if "unsigned" in p:
                unsigned = p["unsigned"]
                if "age" in unsigned:
                    p["age"] = unsigned["age"]
            if "age" in p:
                p["age_ts"] = request_time - int(p["age"])
                del p["age"]

            event = event_from_pdu_json(p)
            room_id = event.room_id
            pdus_by_room.setdefault(room_id, []).append(event)

        pdu_results = {}

        # we can process different rooms in parallel (which is useful if they
        # require callouts to other servers to fetch missing events), but
        # impose a limit to avoid going too crazy with ram/cpu.
        @defer.inlineCallbacks
        def process_pdus_for_room(room_id):
            logger.debug("Processing PDUs for %s", room_id)
            for pdu in pdus_by_room[room_id]:
                event_id = pdu.event_id
                try:
                    yield self._handle_received_pdu(
                        transaction.origin, pdu
                    )
                    pdu_results[event_id] = {}
                except FederationError as e:
                    logger.warn("Error handling PDU %s: %s", event_id, e)
                    pdu_results[event_id] = {"error": str(e)}
                except Exception as e:
                    pdu_results[event_id] = {"error": str(e)}
                    logger.exception("Failed to handle PDU %s", event_id)

        yield async.concurrently_execute(
            process_pdus_for_room, pdus_by_room.keys(),
            TRANSACTION_CONCURRENCY_LIMIT,
        )

        if hasattr(transaction, "edus"):
            for edu in (Edu(**x) for x in transaction.edus):
                yield self.received_edu(
                    transaction.origin,
                    edu.edu_type,
                    edu.content
                )

        pdu_failures = getattr(transaction, "pdu_failures", [])
        for failure in pdu_failures:
            logger.info("Got failure %r", failure)

        response = {
            "pdus": pdu_results,
        }

        logger.debug("Returning: %s", str(response))

        yield self.transaction_actions.set_response(
            transaction,
            200, response
        )
        defer.returnValue((200, response))
Ejemplo n.º 45
0
        def send_request(destination):
            time_now = self._clock.time_msec()
            _, content = yield self.transport_layer.send_join(
                destination=destination,
                room_id=pdu.room_id,
                event_id=pdu.event_id,
                content=pdu.get_pdu_json(time_now),
            )

            logger.debug("Got content: %s", content)

            state = [
                event_from_pdu_json(p, event_format_version, outlier=True)
                for p in content.get("state", [])
            ]

            auth_chain = [
                event_from_pdu_json(p, event_format_version, outlier=True)
                for p in content.get("auth_chain", [])
            ]

            pdus = {
                p.event_id: p
                for p in itertools.chain(state, auth_chain)
            }

            room_version = None
            for e in state:
                if (e.type, e.state_key) == (EventTypes.Create, ""):
                    room_version = e.content.get(
                        "room_version", RoomVersions.V1.identifier
                    )
                    break

            if room_version is None:
                # If the state doesn't have a create event then the room is
                # invalid, and it would fail auth checks anyway.
                raise SynapseError(400, "No create event in state")

            valid_pdus = yield self._check_sigs_and_hash_and_fetch(
                destination, list(pdus.values()),
                outlier=True,
                room_version=room_version,
            )

            valid_pdus_map = {
                p.event_id: p
                for p in valid_pdus
            }

            # NB: We *need* to copy to ensure that we don't have multiple
            # references being passed on, as that causes... issues.
            signed_state = [
                copy.copy(valid_pdus_map[p.event_id])
                for p in state
                if p.event_id in valid_pdus_map
            ]

            signed_auth = [
                valid_pdus_map[p.event_id]
                for p in auth_chain
                if p.event_id in valid_pdus_map
            ]

            # NB: We *need* to copy to ensure that we don't have multiple
            # references being passed on, as that causes... issues.
            for s in signed_state:
                s.internal_metadata = copy.deepcopy(s.internal_metadata)

            check_authchain_validity(signed_auth)

            defer.returnValue({
                "state": signed_state,
                "auth_chain": signed_auth,
                "origin": destination,
            })
Ejemplo n.º 46
0
 def on_invite_request(self, origin, content):
     pdu = event_from_pdu_json(content)
     ret_pdu = yield self.handler.on_invite_request(origin, pdu)
     time_now = self._clock.time_msec()
     defer.returnValue((200, {"event": ret_pdu.get_pdu_json(time_now)}))
Ejemplo n.º 47
0
    def get_pdu(self, destinations, event_id, outlier=False, timeout=None):
        """Requests the PDU with given origin and ID from the remote home
        servers.

        Will attempt to get the PDU from each destination in the list until
        one succeeds.

        Args:
            destinations (list): Which home servers to query
            event_id (str): event to fetch
            outlier (bool): Indicates whether the PDU is an `outlier`, i.e. if
                it's from an arbitary point in the context as opposed to part
                of the current block of PDUs. Defaults to `False`
            timeout (int): How long to try (in ms) each destination for before
                moving to the next destination. None indicates no timeout.

        Returns:
            Deferred: Results in the requested PDU.
        """

        # TODO: Rate limit the number of times we try and get the same event.

        ev = self._get_pdu_cache.get(event_id)
        if ev:
            defer.returnValue(ev)

        pdu_attempts = self.pdu_destination_tried.setdefault(event_id, {})

        signed_pdu = None
        for destination in destinations:
            now = self._clock.time_msec()
            last_attempt = pdu_attempts.get(destination, 0)
            if last_attempt + PDU_RETRY_TIME_MS > now:
                continue

            try:
                transaction_data = yield self.transport_layer.get_event(
                    destination, event_id, timeout=timeout,
                )

                logger.debug("transaction_data %r", transaction_data)

                pdu_list = [
                    event_from_pdu_json(p, outlier=outlier)
                    for p in transaction_data["pdus"]
                ]

                if pdu_list and pdu_list[0]:
                    pdu = pdu_list[0]

                    # Check signatures are correct.
                    signed_pdu = yield self._check_sigs_and_hash(pdu)

                    break

                pdu_attempts[destination] = now

            except SynapseError as e:
                logger.info(
                    "Failed to get PDU %s from %s because %s",
                    event_id, destination, e,
                )
            except NotRetryingDestination as e:
                logger.info(str(e))
                continue
            except FederationDeniedError as e:
                logger.info(str(e))
                continue
            except Exception as e:
                pdu_attempts[destination] = now

                logger.info(
                    "Failed to get PDU %s from %s because %s",
                    event_id, destination, e,
                )
                continue

        if signed_pdu:
            self._get_pdu_cache[event_id] = signed_pdu

        defer.returnValue(signed_pdu)
Ejemplo n.º 48
0
    def get_state_for_room(self, destination, room_id, event_id):
        """Requests all of the room state at a given event from a remote home server.

        Args:
            destination (str): The remote homeserver to query for the state.
            room_id (str): The id of the room we're interested in.
            event_id (str): The id of the event we want the state at.

        Returns:
            Deferred[Tuple[List[EventBase], List[EventBase]]]:
                A list of events in the state, and a list of events in the auth chain
                for the given event.
        """
        try:
            # First we try and ask for just the IDs, as thats far quicker if
            # we have most of the state and auth_chain already.
            # However, this may 404 if the other side has an old synapse.
            result = yield self.transport_layer.get_room_state_ids(
                destination, room_id, event_id=event_id,
            )

            state_event_ids = result["pdu_ids"]
            auth_event_ids = result.get("auth_chain_ids", [])

            fetched_events, failed_to_fetch = yield self.get_events(
                [destination], room_id, set(state_event_ids + auth_event_ids)
            )

            if failed_to_fetch:
                logger.warn("Failed to get %r", failed_to_fetch)

            event_map = {
                ev.event_id: ev for ev in fetched_events
            }

            pdus = [event_map[e_id] for e_id in state_event_ids if e_id in event_map]
            auth_chain = [
                event_map[e_id] for e_id in auth_event_ids if e_id in event_map
            ]

            auth_chain.sort(key=lambda e: e.depth)

            defer.returnValue((pdus, auth_chain))
        except HttpResponseException as e:
            if e.code == 400 or e.code == 404:
                logger.info("Failed to use get_room_state_ids API, falling back")
            else:
                raise e

        result = yield self.transport_layer.get_room_state(
            destination, room_id, event_id=event_id,
        )

        pdus = [
            event_from_pdu_json(p, outlier=True) for p in result["pdus"]
        ]

        auth_chain = [
            event_from_pdu_json(p, outlier=True)
            for p in result.get("auth_chain", [])
        ]

        seen_events = yield self.store.get_events([
            ev.event_id for ev in itertools.chain(pdus, auth_chain)
        ])

        signed_pdus = yield self._check_sigs_and_hash_and_fetch(
            destination,
            [p for p in pdus if p.event_id not in seen_events],
            outlier=True
        )
        signed_pdus.extend(
            seen_events[p.event_id] for p in pdus if p.event_id in seen_events
        )

        signed_auth = yield self._check_sigs_and_hash_and_fetch(
            destination,
            [p for p in auth_chain if p.event_id not in seen_events],
            outlier=True
        )
        signed_auth.extend(
            seen_events[p.event_id] for p in auth_chain if p.event_id in seen_events
        )

        signed_auth.sort(key=lambda e: e.depth)

        defer.returnValue((signed_pdus, signed_auth))
Ejemplo n.º 49
0
        def send_request(destination):
            time_now = self._clock.time_msec()
            _, content = yield self.transport_layer.send_join(
                destination=destination,
                room_id=pdu.room_id,
                event_id=pdu.event_id,
                content=pdu.get_pdu_json(time_now),
            )

            logger.debug("Got content: %s", content)

            state = [
                event_from_pdu_json(p, outlier=True)
                for p in content.get("state", [])
            ]

            auth_chain = [
                event_from_pdu_json(p, outlier=True)
                for p in content.get("auth_chain", [])
            ]

            pdus = {
                p.event_id: p
                for p in itertools.chain(state, auth_chain)
            }

            valid_pdus = yield self._check_sigs_and_hash_and_fetch(
                destination, list(pdus.values()),
                outlier=True,
            )

            valid_pdus_map = {
                p.event_id: p
                for p in valid_pdus
            }

            # NB: We *need* to copy to ensure that we don't have multiple
            # references being passed on, as that causes... issues.
            signed_state = [
                copy.copy(valid_pdus_map[p.event_id])
                for p in state
                if p.event_id in valid_pdus_map
            ]

            signed_auth = [
                valid_pdus_map[p.event_id]
                for p in auth_chain
                if p.event_id in valid_pdus_map
            ]

            # NB: We *need* to copy to ensure that we don't have multiple
            # references being passed on, as that causes... issues.
            for s in signed_state:
                s.internal_metadata = copy.deepcopy(s.internal_metadata)

            check_authchain_validity(signed_auth)

            defer.returnValue({
                "state": signed_state,
                "auth_chain": signed_auth,
                "origin": destination,
            })
Ejemplo n.º 50
0
    def _handle_incoming_transaction(self, origin, transaction, request_time):
        """ Process an incoming transaction and return the HTTP response

        Args:
            origin (unicode): the server making the request
            transaction (Transaction): incoming transaction
            request_time (int): timestamp that the HTTP request arrived at

        Returns:
            Deferred[(int, object)]: http response code and body
        """
        response = yield self.transaction_actions.have_responded(origin, transaction)

        if response:
            logger.debug(
                "[%s] We've already responded to this request",
                transaction.transaction_id
            )
            defer.returnValue(response)
            return

        logger.debug("[%s] Transaction is new", transaction.transaction_id)

        # Reject if PDU count > 50 and EDU count > 100
        if (len(transaction.pdus) > 50
                or (hasattr(transaction, "edus") and len(transaction.edus) > 100)):

            logger.info(
                "Transaction PDU or EDU count too large. Returning 400",
            )

            response = {}
            yield self.transaction_actions.set_response(
                origin,
                transaction,
                400, response
            )
            defer.returnValue((400, response))

        received_pdus_counter.inc(len(transaction.pdus))

        origin_host, _ = parse_server_name(origin)

        pdus_by_room = {}

        for p in transaction.pdus:
            if "unsigned" in p:
                unsigned = p["unsigned"]
                if "age" in unsigned:
                    p["age"] = unsigned["age"]
            if "age" in p:
                p["age_ts"] = request_time - int(p["age"])
                del p["age"]

            # We try and pull out an event ID so that if later checks fail we
            # can log something sensible. We don't mandate an event ID here in
            # case future event formats get rid of the key.
            possible_event_id = p.get("event_id", "<Unknown>")

            # Now we get the room ID so that we can check that we know the
            # version of the room.
            room_id = p.get("room_id")
            if not room_id:
                logger.info(
                    "Ignoring PDU as does not have a room_id. Event ID: %s",
                    possible_event_id,
                )
                continue

            try:
                room_version = yield self.store.get_room_version(room_id)
            except NotFoundError:
                logger.info("Ignoring PDU for unknown room_id: %s", room_id)
                continue

            try:
                format_ver = room_version_to_event_format(room_version)
            except UnsupportedRoomVersionError:
                # this can happen if support for a given room version is withdrawn,
                # so that we still get events for said room.
                logger.info(
                    "Ignoring PDU for room %s with unknown version %s",
                    room_id,
                    room_version,
                )
                continue

            event = event_from_pdu_json(p, format_ver)
            pdus_by_room.setdefault(room_id, []).append(event)

        pdu_results = {}

        # we can process different rooms in parallel (which is useful if they
        # require callouts to other servers to fetch missing events), but
        # impose a limit to avoid going too crazy with ram/cpu.

        @defer.inlineCallbacks
        def process_pdus_for_room(room_id):
            logger.debug("Processing PDUs for %s", room_id)
            try:
                yield self.check_server_matches_acl(origin_host, room_id)
            except AuthError as e:
                logger.warn(
                    "Ignoring PDUs for room %s from banned server", room_id,
                )
                for pdu in pdus_by_room[room_id]:
                    event_id = pdu.event_id
                    pdu_results[event_id] = e.error_dict()
                return

            for pdu in pdus_by_room[room_id]:
                event_id = pdu.event_id
                with nested_logging_context(event_id):
                    try:
                        yield self._handle_received_pdu(
                            origin, pdu
                        )
                        pdu_results[event_id] = {}
                    except FederationError as e:
                        logger.warn("Error handling PDU %s: %s", event_id, e)
                        pdu_results[event_id] = {"error": str(e)}
                    except Exception as e:
                        f = failure.Failure()
                        pdu_results[event_id] = {"error": str(e)}
                        logger.error(
                            "Failed to handle PDU %s",
                            event_id,
                            exc_info=(f.type, f.value, f.getTracebackObject()),
                        )

        yield concurrently_execute(
            process_pdus_for_room, pdus_by_room.keys(),
            TRANSACTION_CONCURRENCY_LIMIT,
        )

        if hasattr(transaction, "edus"):
            for edu in (Edu(**x) for x in transaction.edus):
                yield self.received_edu(
                    origin,
                    edu.edu_type,
                    edu.content
                )

        response = {
            "pdus": pdu_results,
        }

        logger.debug("Returning: %s", str(response))

        yield self.transaction_actions.set_response(
            origin,
            transaction,
            200, response
        )
        defer.returnValue((200, response))
Ejemplo n.º 51
0
 def on_send_leave_request(self, origin, content):
     logger.debug("on_send_leave_request: content: %s", content)
     pdu = event_from_pdu_json(content)
     logger.debug("on_send_leave_request: pdu sigs: %s", pdu.signatures)
     yield self.handler.on_send_leave_request(origin, pdu)
     defer.returnValue((200, {}))