diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index b48eaf78..1a121987 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -329,6 +329,12 @@ func SendJoin( JSON: jsonerror.NotFound("Room does not exist"), } } + if !stateAndAuthChainResponse.StateKnown { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: jsonerror.Forbidden("State not known"), + } + } // Check if the user is already in the room. If they're already in then // there isn't much point in sending another join event into the room. diff --git a/federationapi/routing/state.go b/federationapi/routing/state.go index 6fdce20c..5377eb88 100644 --- a/federationapi/routing/state.go +++ b/federationapi/routing/state.go @@ -135,6 +135,12 @@ func getState( return nil, nil, &resErr } + if !response.StateKnown { + return nil, nil, &util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("State not known"), + } + } if response.IsRejected { return nil, nil, &util.JSONResponse{ Code: http.StatusNotFound, diff --git a/roomserver/api/query.go b/roomserver/api/query.go index c8e6f9dc..32d63bb5 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -227,6 +227,7 @@ type QueryStateAndAuthChainResponse struct { // Do all the previous events exist on this roomserver? // If some of previous events do not exist this will be false and StateEvents will be empty. PrevEventsExist bool `json:"prev_events_exist"` + StateKnown bool `json:"state_known"` // The state and auth chain events that were requested. // The lists will be in an arbitrary order. StateEvents []*gomatrixserverlib.HeaderedEvent `json:"state_events"` diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index f5d8c2d4..6dce2bc3 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -503,10 +503,11 @@ func (r *Queryer) QueryStateAndAuthChain( } var stateEvents []*gomatrixserverlib.Event - stateEvents, rejected, err := r.loadStateAtEventIDs(ctx, info, request.PrevEventIDs) + stateEvents, rejected, stateMissing, err := r.loadStateAtEventIDs(ctx, info, request.PrevEventIDs) if err != nil { return err } + response.StateKnown = !stateMissing response.IsRejected = rejected response.PrevEventsExist = true @@ -542,15 +543,18 @@ func (r *Queryer) QueryStateAndAuthChain( return err } -func (r *Queryer) loadStateAtEventIDs(ctx context.Context, roomInfo *types.RoomInfo, eventIDs []string) ([]*gomatrixserverlib.Event, bool, error) { +// first bool: is rejected, second bool: state missing +func (r *Queryer) loadStateAtEventIDs(ctx context.Context, roomInfo *types.RoomInfo, eventIDs []string) ([]*gomatrixserverlib.Event, bool, bool, error) { roomState := state.NewStateResolution(r.DB, roomInfo) prevStates, err := r.DB.StateAtEventIDs(ctx, eventIDs) if err != nil { switch err.(type) { case types.MissingEventError: - return nil, false, nil + return nil, false, true, nil + case types.MissingStateError: + return nil, false, true, nil default: - return nil, false, err + return nil, false, false, err } } // Currently only used on /state and /state_ids @@ -567,12 +571,11 @@ func (r *Queryer) loadStateAtEventIDs(ctx context.Context, roomInfo *types.RoomI ctx, prevStates, ) if err != nil { - return nil, rejected, err + return nil, rejected, false, err } events, err := helpers.LoadStateEvents(ctx, r.DB, stateEntries) - - return events, rejected, err + return events, rejected, false, err } type eventsFromIDs func(context.Context, []string) ([]types.Event, error)