From eda4efcf1315ee2b80872a31105cd40ade09e756 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Fri, 1 May 2020 17:25:45 +0100 Subject: [PATCH] Add directory lookups of aliases --- federationsender/api/api.go | 6 ++++ federationsender/api/perform.go | 26 +++++++++++++++ federationsender/internal/perform.go | 19 +++++++++++ roomserver/internal/perform_join.go | 49 +++++++++++++++++++++------- 4 files changed, 88 insertions(+), 12 deletions(-) diff --git a/federationsender/api/api.go b/federationsender/api/api.go index 10dc66da2..678f02e68 100644 --- a/federationsender/api/api.go +++ b/federationsender/api/api.go @@ -8,6 +8,12 @@ import ( // FederationSenderInternalAPI is used to query information from the federation sender. type FederationSenderInternalAPI interface { + // PerformDirectoryLookup looks up a remote room ID from a room alias. + PerformDirectoryLookup( + ctx context.Context, + request *PerformDirectoryLookupRequest, + response *PerformDirectoryLookupResponse, + ) error // Query the joined hosts and the membership events accounting for their participation in a room. // Note that if a server has multiple users in the room, it will have multiple entries in the returned slice. // See `QueryJoinedHostServerNamesInRoom` for a de-duplicated version. diff --git a/federationsender/api/perform.go b/federationsender/api/perform.go index 87736f294..4b90aa6f9 100644 --- a/federationsender/api/perform.go +++ b/federationsender/api/perform.go @@ -9,6 +9,9 @@ import ( ) const ( + // FederationSenderPerformJoinRequestPath is the HTTP path for the PerformJoinRequest API. + FederationSenderPerformDirectoryLookupRequestPath = "/api/federationsender/performDirectoryLookup" + // FederationSenderPerformJoinRequestPath is the HTTP path for the PerformJoinRequest API. FederationSenderPerformJoinRequestPath = "/api/federationsender/performJoinRequest" @@ -16,6 +19,29 @@ const ( FederationSenderPerformLeaveRequestPath = "/api/federationsender/performLeaveRequest" ) +type PerformDirectoryLookupRequest struct { + RoomAlias string `json:"room_alias"` + ServerName gomatrixserverlib.ServerName `json:"server_name"` +} + +type PerformDirectoryLookupResponse struct { + RoomID string `json:"room_id"` + ServerNames []gomatrixserverlib.ServerName `json:"server_names"` +} + +// Handle an instruction to make_join & send_join with a remote server. +func (h *httpFederationSenderInternalAPI) PerformDirectoryLookup( + ctx context.Context, + request *PerformDirectoryLookupRequest, + response *PerformDirectoryLookupResponse, +) error { + span, ctx := opentracing.StartSpanFromContext(ctx, "PerformDirectoryLookup") + defer span.Finish() + + apiURL := h.federationSenderURL + FederationSenderPerformDirectoryLookupRequestPath + return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response) +} + type PerformJoinRequest struct { RoomID string `json:"room_id"` UserID string `json:"user_id"` diff --git a/federationsender/internal/perform.go b/federationsender/internal/perform.go index 64774b65d..d77aca66c 100644 --- a/federationsender/internal/perform.go +++ b/federationsender/internal/perform.go @@ -11,6 +11,25 @@ import ( "github.com/matrix-org/gomatrixserverlib" ) +// PerformLeaveRequest implements api.FederationSenderInternalAPI +func (r *FederationSenderInternalAPI) PerformDirectoryLookup( + ctx context.Context, + request *api.PerformDirectoryLookupRequest, + response *api.PerformDirectoryLookupResponse, +) (err error) { + dir, err := r.federation.LookupRoomAlias( + ctx, + request.ServerName, + request.RoomAlias, + ) + if err != nil { + return err + } + response.RoomID = dir.RoomID + response.ServerNames = dir.Servers + return nil +} + // PerformJoinRequest implements api.FederationSenderInternalAPI func (r *FederationSenderInternalAPI) PerformJoin( ctx context.Context, diff --git a/roomserver/internal/perform_join.go b/roomserver/internal/perform_join.go index e9e33d933..b56a90e8d 100644 --- a/roomserver/internal/perform_join.go +++ b/roomserver/internal/perform_join.go @@ -21,10 +21,10 @@ func (r *RoomserverInternalAPI) PerformJoin( ) error { _, domain, err := gomatrixserverlib.SplitID('@', req.UserID) if err != nil { - return fmt.Errorf("supplied user ID %q in incorrect format", req.UserID) + return fmt.Errorf("Supplied user ID %q in incorrect format", req.UserID) } if domain != r.Cfg.Matrix.ServerName { - return fmt.Errorf("user ID %q does not belong to this homeserver", req.UserID) + return fmt.Errorf("User %q does not belong to this homeserver", req.UserID) } if strings.HasPrefix(req.RoomIDOrAlias, "!") { return r.performJoinRoomByID(ctx, req, res) @@ -32,7 +32,7 @@ func (r *RoomserverInternalAPI) PerformJoin( if strings.HasPrefix(req.RoomIDOrAlias, "#") { return r.performJoinRoomByAlias(ctx, req, res) } - return fmt.Errorf("unexpected sigil on room %q", req.RoomIDOrAlias) + return fmt.Errorf("Room ID or alias %q is invalid", req.RoomIDOrAlias) } func (r *RoomserverInternalAPI) performJoinRoomByAlias( @@ -43,14 +43,39 @@ func (r *RoomserverInternalAPI) performJoinRoomByAlias( // Get the domain part of the room alias. _, domain, err := gomatrixserverlib.SplitID('#', req.RoomIDOrAlias) if err != nil { - return fmt.Errorf("supplied room alias %q in incorrect format", req.RoomIDOrAlias) + return fmt.Errorf("Alias %q is not in the correct format", req.RoomIDOrAlias) } req.ServerNames = append(req.ServerNames, domain) - // Look up if we know this room alias. - roomID, err := r.DB.GetRoomIDForAlias(ctx, req.RoomIDOrAlias) - if err != nil { - return err + // Check if this alias matches our own server configuration. If it + // doesn't then we'll need to try a federated join. + var roomID string + if domain != r.Cfg.Matrix.ServerName { + // The alias isn't owned by us, so we will eed to try joining using + // a remote server. + dirReq := fsAPI.PerformDirectoryLookupRequest{ + RoomAlias: req.RoomIDOrAlias, // the room alias to lookup + ServerName: domain, // the server to ask + } + dirRes := fsAPI.PerformDirectoryLookupResponse{} + err = r.fsAPI.PerformDirectoryLookup(ctx, &dirReq, &dirRes) + if err != nil { + logrus.WithError(err).Errorf("error looking up alias %q", req.RoomIDOrAlias) + return fmt.Errorf("Looking up alias %q over federation failed: %w", req.RoomIDOrAlias, err) + } + roomID = dirRes.RoomID + req.ServerNames = append(req.ServerNames, dirRes.ServerNames...) + } else { + // Otherwise, look up if we know this room alias locally. + roomID, err = r.DB.GetRoomIDForAlias(ctx, req.RoomIDOrAlias) + if err != nil { + return fmt.Errorf("Lookup room alias %q failed: %w", req.RoomIDOrAlias, err) + } + } + + // If the room ID is empty then we failed to look up the alias. + if roomID == "" { + return fmt.Errorf("Alias %q not found", req.RoomIDOrAlias) } // If we do, then pluck out the room ID and continue the join. @@ -68,7 +93,7 @@ func (r *RoomserverInternalAPI) performJoinRoomByID( // Get the domain part of the room ID. _, domain, err := gomatrixserverlib.SplitID('!', req.RoomIDOrAlias) if err != nil { - return fmt.Errorf("supplied room ID %q in incorrect format", req.RoomIDOrAlias) + return fmt.Errorf("Room ID %q is invalid", req.RoomIDOrAlias) } req.ServerNames = append(req.ServerNames, domain) @@ -135,7 +160,7 @@ func (r *RoomserverInternalAPI) performJoinRoomByID( // room. If it is then there's nothing more to do - the room just // hasn't been created yet. if domain == r.Cfg.Matrix.ServerName { - return fmt.Errorf("room ID %q does not exist", req.RoomIDOrAlias) + return fmt.Errorf("Room ID %q does not exist", req.RoomIDOrAlias) } // Try joining by all of the supplied server names. @@ -164,13 +189,13 @@ func (r *RoomserverInternalAPI) performJoinRoomByID( // servers then return an error saying such. if !joined { return fmt.Errorf( - "failed to join %q using %d server(s)", + "Failed to join %q using %d server(s)", req.RoomIDOrAlias, len(req.ServerNames), ) } default: - return fmt.Errorf("error joining room %q: %w", req.RoomIDOrAlias, err) + return fmt.Errorf("Error joining room %q: %w", req.RoomIDOrAlias, err) } return nil