summary refs log tree commit diff
path: root/synapse/handlers/federation.py
diff options
context:
space:
mode:
authorAmber Brown <hawkowl@atleastfornow.net>2018-06-27 11:27:32 +0100
committerAmber Brown <hawkowl@atleastfornow.net>2018-06-27 11:27:32 +0100
commit77078d6c8ef11e2401406edc1ca340e0d7779267 (patch)
treedfb1c69b80cd017f5406de5b5a1bc9ddbf16a09b /synapse/handlers/federation.py
parentBetter testing framework for homeserver-using things (#3446) (diff)
downloadsynapse-77078d6c8ef11e2401406edc1ca340e0d7779267.tar.xz
handle federation not telling us about prev_events
Diffstat (limited to 'synapse/handlers/federation.py')
-rw-r--r--synapse/handlers/federation.py87
1 files changed, 62 insertions, 25 deletions
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index b6f8d4cf82..250a5509d8 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -44,6 +44,7 @@ from synapse.util.frozenutils import unfreeze
 from synapse.crypto.event_signing import (
     compute_event_signature, add_hashes_and_signatures,
 )
+from synapse.state import resolve_events_with_factory
 from synapse.types import UserID, get_domain_from_id
 
 from synapse.events.utils import prune_event
@@ -89,7 +90,9 @@ class FederationHandler(BaseHandler):
 
     @defer.inlineCallbacks
     @log_function
-    def on_receive_pdu(self, origin, pdu, get_missing=True):
+    def on_receive_pdu(
+            self, origin, pdu, get_missing=True, sent_to_us_directly=False,
+    ):
         """ Process a PDU received via a federation /send/ transaction, or
         via backfill of missing prev_events
 
@@ -163,7 +166,7 @@ class FederationHandler(BaseHandler):
                     "Ignoring PDU %s for room %s from %s as we've left the room!",
                     pdu.event_id, pdu.room_id, origin,
                 )
-                return
+                defer.returnValue(None)
 
         state = None
 
@@ -225,26 +228,54 @@ class FederationHandler(BaseHandler):
                         list(prevs - seen)[:5],
                     )
 
-            if prevs - seen:
-                logger.info(
-                    "Still missing %d events for room %r: %r...",
-                    len(prevs - seen), pdu.room_id, list(prevs - seen)[:5]
+            if sent_to_us_directly and prevs - seen:
+                # If they have sent it to us directly, and the server
+                # isn't telling us about the auth events that it's
+                # made a message referencing, we explode
+                raise FederationError(
+                    "ERROR",
+                    403,
+                    ("Your server isn't divulging details about prev_events "
+                     "referenced in this event."),
+                    affected=pdu.event_id,
                 )
-                fetch_state = True
+            elif prevs - seen:
+                # If we're walking back up the chain to fetch it, then
+                # try and find the states. If we can't get the states,
+                # discard it.
+                state_groups = []
+                auth_chains = set()
+                try:
+                    # Get the ones we know about
+                    ours = yield self.store.get_state_groups(pdu.room_id, list(seen))
+                    state_groups.append(ours)
 
-        if fetch_state:
-            # We need to get the state at this event, since we haven't
-            # processed all the prev events.
-            logger.debug(
-                "_handle_new_pdu getting state for %s",
-                pdu.room_id
-            )
-            try:
-                state, auth_chain = yield self.replication_layer.get_state_for_room(
-                    origin, pdu.room_id, pdu.event_id,
-                )
-            except Exception:
-                logger.exception("Failed to get state for event: %s", pdu.event_id)
+                    for p in prevs - seen:
+                        state, auth_chain = yield self.replication_layer.get_state_for_room(
+                            origin, pdu.room_id, p
+                        )
+                        auth_chains.update(auth_chain)
+                        state_group = {
+                            (x.type, x.state_key): x.event_id for x in state
+                        }
+                        state_groups.append(state_group)
+
+                    def fetch(ev_ids):
+                        return self.store.get_events(
+                            ev_ids, get_prev_content=False, check_redacted=False,
+                        )
+
+                    state = yield resolve_events_with_factory(state_groups, {pdu.event_id: pdu}, fetch)
+
+                    state = yield self.store.get_events(state.values())
+                    state = state.values()
+                except Exception:
+                    raise FederationError(
+                        "ERROR",
+                        403,
+                        "We can't get valid state history.",
+                        affected=pdu.event_id,
+                    )
 
         yield self._process_received_pdu(
             origin,
@@ -322,11 +353,17 @@ class FederationHandler(BaseHandler):
 
         for e in missing_events:
             logger.info("Handling found event %s", e.event_id)
-            yield self.on_receive_pdu(
-                origin,
-                e,
-                get_missing=False
-            )
+            try:
+                yield self.on_receive_pdu(
+                    origin,
+                    e,
+                    get_missing=False
+                )
+            except FederationError as e:
+                if e.code == 403:
+                    logger.warn("Event %s failed history check.")
+                else:
+                    raise
 
     @log_function
     @defer.inlineCallbacks