package inthttp import ( "context" "errors" "net/http" "github.com/matrix-org/dendrite/federationapi/api" "github.com/matrix-org/dendrite/internal/caching" "github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/gomatrix" "github.com/matrix-org/gomatrixserverlib" ) // HTTP paths for the internal HTTP API const ( FederationAPIQueryJoinedHostServerNamesInRoomPath = "/federationapi/queryJoinedHostServerNamesInRoom" FederationAPIQueryServerKeysPath = "/federationapi/queryServerKeys" FederationAPIPerformDirectoryLookupRequestPath = "/federationapi/performDirectoryLookup" FederationAPIPerformJoinRequestPath = "/federationapi/performJoinRequest" FederationAPIPerformLeaveRequestPath = "/federationapi/performLeaveRequest" FederationAPIPerformInviteRequestPath = "/federationapi/performInviteRequest" FederationAPIPerformOutboundPeekRequestPath = "/federationapi/performOutboundPeekRequest" FederationAPIPerformBroadcastEDUPath = "/federationapi/performBroadcastEDU" FederationAPIGetUserDevicesPath = "/federationapi/client/getUserDevices" FederationAPIClaimKeysPath = "/federationapi/client/claimKeys" FederationAPIQueryKeysPath = "/federationapi/client/queryKeys" FederationAPIBackfillPath = "/federationapi/client/backfill" FederationAPILookupStatePath = "/federationapi/client/lookupState" FederationAPILookupStateIDsPath = "/federationapi/client/lookupStateIDs" FederationAPILookupMissingEventsPath = "/federationapi/client/lookupMissingEvents" FederationAPIGetEventPath = "/federationapi/client/getEvent" FederationAPILookupServerKeysPath = "/federationapi/client/lookupServerKeys" FederationAPIEventRelationshipsPath = "/federationapi/client/msc2836eventRelationships" FederationAPISpacesSummaryPath = "/federationapi/client/msc2946spacesSummary" FederationAPIGetEventAuthPath = "/federationapi/client/getEventAuth" FederationAPIInputPublicKeyPath = "/federationapi/inputPublicKey" FederationAPIQueryPublicKeyPath = "/federationapi/queryPublicKey" ) // NewFederationAPIClient creates a FederationInternalAPI implemented by talking to a HTTP POST API. // If httpClient is nil an error is returned func NewFederationAPIClient(federationSenderURL string, httpClient *http.Client, cache caching.ServerKeyCache) (api.FederationInternalAPI, error) { if httpClient == nil { return nil, errors.New("NewFederationInternalAPIHTTP: httpClient is ") } return &httpFederationInternalAPI{ federationAPIURL: federationSenderURL, httpClient: httpClient, cache: cache, }, nil } type httpFederationInternalAPI struct { federationAPIURL string httpClient *http.Client cache caching.ServerKeyCache } // Handle an instruction to make_leave & send_leave with a remote server. func (h *httpFederationInternalAPI) PerformLeave( ctx context.Context, request *api.PerformLeaveRequest, response *api.PerformLeaveResponse, ) error { return httputil.CallInternalRPCAPI( "PerformLeave", h.federationAPIURL+FederationAPIPerformLeaveRequestPath, h.httpClient, ctx, request, response, ) } // Handle sending an invite to a remote server. func (h *httpFederationInternalAPI) PerformInvite( ctx context.Context, request *api.PerformInviteRequest, response *api.PerformInviteResponse, ) error { return httputil.CallInternalRPCAPI( "PerformInvite", h.federationAPIURL+FederationAPIPerformInviteRequestPath, h.httpClient, ctx, request, response, ) } // Handle starting a peek on a remote server. func (h *httpFederationInternalAPI) PerformOutboundPeek( ctx context.Context, request *api.PerformOutboundPeekRequest, response *api.PerformOutboundPeekResponse, ) error { return httputil.CallInternalRPCAPI( "PerformOutboundPeek", h.federationAPIURL+FederationAPIPerformOutboundPeekRequestPath, h.httpClient, ctx, request, response, ) } // QueryJoinedHostServerNamesInRoom implements FederationInternalAPI func (h *httpFederationInternalAPI) QueryJoinedHostServerNamesInRoom( ctx context.Context, request *api.QueryJoinedHostServerNamesInRoomRequest, response *api.QueryJoinedHostServerNamesInRoomResponse, ) error { return httputil.CallInternalRPCAPI( "QueryJoinedHostServerNamesInRoom", h.federationAPIURL+FederationAPIQueryJoinedHostServerNamesInRoomPath, h.httpClient, ctx, request, response, ) } // Handle an instruction to make_join & send_join with a remote server. func (h *httpFederationInternalAPI) PerformJoin( ctx context.Context, request *api.PerformJoinRequest, response *api.PerformJoinResponse, ) { if err := httputil.CallInternalRPCAPI( "PerformJoinRequest", h.federationAPIURL+FederationAPIPerformJoinRequestPath, h.httpClient, ctx, request, response, ); err != nil { response.LastError = &gomatrix.HTTPError{ Message: err.Error(), Code: 0, WrappedError: err, } } } // Handle an instruction to make_join & send_join with a remote server. func (h *httpFederationInternalAPI) PerformDirectoryLookup( ctx context.Context, request *api.PerformDirectoryLookupRequest, response *api.PerformDirectoryLookupResponse, ) error { return httputil.CallInternalRPCAPI( "PerformDirectoryLookup", h.federationAPIURL+FederationAPIPerformDirectoryLookupRequestPath, h.httpClient, ctx, request, response, ) } // Handle an instruction to broadcast an EDU to all servers in rooms we are joined to. func (h *httpFederationInternalAPI) PerformBroadcastEDU( ctx context.Context, request *api.PerformBroadcastEDURequest, response *api.PerformBroadcastEDUResponse, ) error { return httputil.CallInternalRPCAPI( "PerformBroadcastEDU", h.federationAPIURL+FederationAPIPerformBroadcastEDUPath, h.httpClient, ctx, request, response, ) } type getUserDevices struct { S gomatrixserverlib.ServerName UserID string } func (h *httpFederationInternalAPI) GetUserDevices( ctx context.Context, s gomatrixserverlib.ServerName, userID string, ) (gomatrixserverlib.RespUserDevices, error) { return httputil.CallInternalProxyAPI[getUserDevices, gomatrixserverlib.RespUserDevices, *api.FederationClientError]( "GetUserDevices", h.federationAPIURL+FederationAPIGetUserDevicesPath, h.httpClient, ctx, &getUserDevices{ S: s, UserID: userID, }, ) } type claimKeys struct { S gomatrixserverlib.ServerName OneTimeKeys map[string]map[string]string } func (h *httpFederationInternalAPI) ClaimKeys( ctx context.Context, s gomatrixserverlib.ServerName, oneTimeKeys map[string]map[string]string, ) (gomatrixserverlib.RespClaimKeys, error) { return httputil.CallInternalProxyAPI[claimKeys, gomatrixserverlib.RespClaimKeys, *api.FederationClientError]( "ClaimKeys", h.federationAPIURL+FederationAPIClaimKeysPath, h.httpClient, ctx, &claimKeys{ S: s, OneTimeKeys: oneTimeKeys, }, ) } type queryKeys struct { S gomatrixserverlib.ServerName Keys map[string][]string } func (h *httpFederationInternalAPI) QueryKeys( ctx context.Context, s gomatrixserverlib.ServerName, keys map[string][]string, ) (gomatrixserverlib.RespQueryKeys, error) { return httputil.CallInternalProxyAPI[queryKeys, gomatrixserverlib.RespQueryKeys, *api.FederationClientError]( "QueryKeys", h.federationAPIURL+FederationAPIQueryKeysPath, h.httpClient, ctx, &queryKeys{ S: s, Keys: keys, }, ) } type backfill struct { S gomatrixserverlib.ServerName RoomID string Limit int EventIDs []string } func (h *httpFederationInternalAPI) Backfill( ctx context.Context, s gomatrixserverlib.ServerName, roomID string, limit int, eventIDs []string, ) (gomatrixserverlib.Transaction, error) { return httputil.CallInternalProxyAPI[backfill, gomatrixserverlib.Transaction, *api.FederationClientError]( "Backfill", h.federationAPIURL+FederationAPIBackfillPath, h.httpClient, ctx, &backfill{ S: s, RoomID: roomID, Limit: limit, EventIDs: eventIDs, }, ) } type lookupState struct { S gomatrixserverlib.ServerName RoomID string EventID string RoomVersion gomatrixserverlib.RoomVersion } func (h *httpFederationInternalAPI) LookupState( ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion, ) (gomatrixserverlib.RespState, error) { return httputil.CallInternalProxyAPI[lookupState, gomatrixserverlib.RespState, *api.FederationClientError]( "LookupState", h.federationAPIURL+FederationAPILookupStatePath, h.httpClient, ctx, &lookupState{ S: s, RoomID: roomID, EventID: eventID, RoomVersion: roomVersion, }, ) } type lookupStateIDs struct { S gomatrixserverlib.ServerName RoomID string EventID string } func (h *httpFederationInternalAPI) LookupStateIDs( ctx context.Context, s gomatrixserverlib.ServerName, roomID, eventID string, ) (gomatrixserverlib.RespStateIDs, error) { return httputil.CallInternalProxyAPI[lookupStateIDs, gomatrixserverlib.RespStateIDs, *api.FederationClientError]( "LookupStateIDs", h.federationAPIURL+FederationAPILookupStateIDsPath, h.httpClient, ctx, &lookupStateIDs{ S: s, RoomID: roomID, EventID: eventID, }, ) } type lookupMissingEvents struct { S gomatrixserverlib.ServerName RoomID string Missing gomatrixserverlib.MissingEvents RoomVersion gomatrixserverlib.RoomVersion } func (h *httpFederationInternalAPI) LookupMissingEvents( ctx context.Context, s gomatrixserverlib.ServerName, roomID string, missing gomatrixserverlib.MissingEvents, roomVersion gomatrixserverlib.RoomVersion, ) (res gomatrixserverlib.RespMissingEvents, err error) { return httputil.CallInternalProxyAPI[lookupMissingEvents, gomatrixserverlib.RespMissingEvents, *api.FederationClientError]( "LookupMissingEvents", h.federationAPIURL+FederationAPILookupMissingEventsPath, h.httpClient, ctx, &lookupMissingEvents{ S: s, RoomID: roomID, Missing: missing, RoomVersion: roomVersion, }, ) } type getEvent struct { S gomatrixserverlib.ServerName EventID string } func (h *httpFederationInternalAPI) GetEvent( ctx context.Context, s gomatrixserverlib.ServerName, eventID string, ) (gomatrixserverlib.Transaction, error) { return httputil.CallInternalProxyAPI[getEvent, gomatrixserverlib.Transaction, *api.FederationClientError]( "GetEvent", h.federationAPIURL+FederationAPIGetEventPath, h.httpClient, ctx, &getEvent{ S: s, EventID: eventID, }, ) } type getEventAuth struct { S gomatrixserverlib.ServerName RoomVersion gomatrixserverlib.RoomVersion RoomID string EventID string } func (h *httpFederationInternalAPI) GetEventAuth( ctx context.Context, s gomatrixserverlib.ServerName, roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string, ) (gomatrixserverlib.RespEventAuth, error) { return httputil.CallInternalProxyAPI[getEventAuth, gomatrixserverlib.RespEventAuth, *api.FederationClientError]( "GetEventAuth", h.federationAPIURL+FederationAPIGetEventAuthPath, h.httpClient, ctx, &getEventAuth{ S: s, RoomVersion: roomVersion, RoomID: roomID, EventID: eventID, }, ) } func (h *httpFederationInternalAPI) QueryServerKeys( ctx context.Context, req *api.QueryServerKeysRequest, res *api.QueryServerKeysResponse, ) error { return httputil.CallInternalRPCAPI( "QueryServerKeys", h.federationAPIURL+FederationAPIQueryServerKeysPath, h.httpClient, ctx, req, res, ) } type lookupServerKeys struct { S gomatrixserverlib.ServerName KeyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp } func (h *httpFederationInternalAPI) LookupServerKeys( ctx context.Context, s gomatrixserverlib.ServerName, keyRequests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp, ) ([]gomatrixserverlib.ServerKeys, error) { return httputil.CallInternalProxyAPI[lookupServerKeys, []gomatrixserverlib.ServerKeys, *api.FederationClientError]( "LookupServerKeys", h.federationAPIURL+FederationAPILookupServerKeysPath, h.httpClient, ctx, &lookupServerKeys{ S: s, KeyRequests: keyRequests, }, ) } type eventRelationships struct { S gomatrixserverlib.ServerName Req gomatrixserverlib.MSC2836EventRelationshipsRequest RoomVer gomatrixserverlib.RoomVersion } func (h *httpFederationInternalAPI) MSC2836EventRelationships( ctx context.Context, s gomatrixserverlib.ServerName, r gomatrixserverlib.MSC2836EventRelationshipsRequest, roomVersion gomatrixserverlib.RoomVersion, ) (res gomatrixserverlib.MSC2836EventRelationshipsResponse, err error) { return httputil.CallInternalProxyAPI[eventRelationships, gomatrixserverlib.MSC2836EventRelationshipsResponse, *api.FederationClientError]( "MSC2836EventRelationships", h.federationAPIURL+FederationAPIEventRelationshipsPath, h.httpClient, ctx, &eventRelationships{ S: s, Req: r, RoomVer: roomVersion, }, ) } type spacesReq struct { S gomatrixserverlib.ServerName SuggestedOnly bool RoomID string } func (h *httpFederationInternalAPI) MSC2946Spaces( ctx context.Context, dst gomatrixserverlib.ServerName, roomID string, suggestedOnly bool, ) (res gomatrixserverlib.MSC2946SpacesResponse, err error) { return httputil.CallInternalProxyAPI[spacesReq, gomatrixserverlib.MSC2946SpacesResponse, *api.FederationClientError]( "MSC2836EventRelationships", h.federationAPIURL+FederationAPISpacesSummaryPath, h.httpClient, ctx, &spacesReq{ S: dst, SuggestedOnly: suggestedOnly, RoomID: roomID, }, ) } func (s *httpFederationInternalAPI) KeyRing() *gomatrixserverlib.KeyRing { // This is a bit of a cheat - we tell gomatrixserverlib that this API is // both the key database and the key fetcher. While this does have the // rather unfortunate effect of preventing gomatrixserverlib from handling // key fetchers directly, we can at least reimplement this behaviour on // the other end of the API. return &gomatrixserverlib.KeyRing{ KeyDatabase: s, KeyFetchers: []gomatrixserverlib.KeyFetcher{}, } } func (s *httpFederationInternalAPI) FetcherName() string { return "httpServerKeyInternalAPI" } func (s *httpFederationInternalAPI) StoreKeys( _ context.Context, results map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, ) error { // Run in a background context - we don't want to stop this work just // because the caller gives up waiting. ctx := context.Background() request := api.InputPublicKeysRequest{ Keys: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult), } response := api.InputPublicKeysResponse{} for req, res := range results { request.Keys[req] = res s.cache.StoreServerKey(req, res) } return s.InputPublicKeys(ctx, &request, &response) } func (s *httpFederationInternalAPI) FetchKeys( _ context.Context, requests map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp, ) (map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult, error) { // Run in a background context - we don't want to stop this work just // because the caller gives up waiting. ctx := context.Background() result := make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult) request := api.QueryPublicKeysRequest{ Requests: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.Timestamp), } response := api.QueryPublicKeysResponse{ Results: make(map[gomatrixserverlib.PublicKeyLookupRequest]gomatrixserverlib.PublicKeyLookupResult), } for req, ts := range requests { if res, ok := s.cache.GetServerKey(req, ts); ok { result[req] = res continue } request.Requests[req] = ts } err := s.QueryPublicKeys(ctx, &request, &response) if err != nil { return nil, err } for req, res := range response.Results { result[req] = res s.cache.StoreServerKey(req, res) } return result, nil } func (h *httpFederationInternalAPI) InputPublicKeys( ctx context.Context, request *api.InputPublicKeysRequest, response *api.InputPublicKeysResponse, ) error { return httputil.CallInternalRPCAPI( "InputPublicKey", h.federationAPIURL+FederationAPIInputPublicKeyPath, h.httpClient, ctx, request, response, ) } func (h *httpFederationInternalAPI) QueryPublicKeys( ctx context.Context, request *api.QueryPublicKeysRequest, response *api.QueryPublicKeysResponse, ) error { return httputil.CallInternalRPCAPI( "QueryPublicKeys", h.federationAPIURL+FederationAPIQueryPublicKeyPath, h.httpClient, ctx, request, response, ) }