Skip to content

Commit

Permalink
New stanza interceptor interface (ortuman#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
ortuman authored Apr 5, 2021
1 parent 21e07ee commit db009f3
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 298 deletions.
39 changes: 27 additions & 12 deletions c2s/in.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,12 @@ func (s *inC2S) processStanza(ctx context.Context, stanza stravaganza.Stanza) er
}
// apply incoming stanza interceptor
tst, err := s.mods.InterceptStanza(ctx, stanza, true)
if err != nil {
switch err {
case nil:
break
case module.ErrInterceptStanzaInterrupted:
return nil // stanza processing interrupted
default:
return err
}
// handle stanza
Expand Down Expand Up @@ -478,24 +483,25 @@ func (s *inC2S) processIQ(ctx context.Context, iq *stravaganza.IQ) error {
}
// apply outgoing stanza interceptor
tst, err := s.mods.InterceptStanza(ctx, iq, false)
if err != nil {
switch err {
case nil:
break
case module.ErrInterceptStanzaInterrupted:
return nil // stanza routing interrupted
default:
return err
}
targets, err := s.router.Route(ctx, tst)
switch err {
case router.ErrResourceNotFound:
return s.sendElement(ctx, stanzaerror.E(stanzaerror.ServiceUnavailable, iq).Element())

case router.ErrRemoteServerNotFound:
return s.sendElement(ctx, stanzaerror.E(stanzaerror.RemoteServerNotFound, iq).Element())

case router.ErrRemoteServerTimeout:
return s.sendElement(ctx, stanzaerror.E(stanzaerror.RemoteServerTimeout, iq).Element())

case router.ErrBlockedSender:
// sender is a blocked JID
if iq.IsGet() || iq.IsSet() {
return s.sendElement(ctx, stanzaerror.E(stanzaerror.ServiceUnavailable, iq).Element())
}

case nil:
return s.postStreamEvent(ctx, event.C2SStreamIQRouted, &event.C2SStreamEventInfo{
ID: s.ID().String(),
Expand All @@ -507,7 +513,6 @@ func (s *inC2S) processIQ(ctx context.Context, iq *stravaganza.IQ) error {
default:
return err
}
return nil
}

func (s *inC2S) processPresence(ctx context.Context, presence *stravaganza.Presence) error {
Expand All @@ -524,7 +529,12 @@ func (s *inC2S) processPresence(ctx context.Context, presence *stravaganza.Prese
if presence.ToJID().IsFullWithUser() {
// apply outgoing stanza interceptor
tst, err := s.mods.InterceptStanza(ctx, presence, false)
if err != nil {
switch err {
case nil:
break
case module.ErrInterceptStanzaInterrupted:
return nil // stanza routing interrupted
default:
return err
}
targets, err := s.router.Route(ctx, tst)
Expand Down Expand Up @@ -565,7 +575,12 @@ func (s *inC2S) processMessage(ctx context.Context, message *stravaganza.Message
sendMsg:
// apply outgoing stanza interceptor
tst, err := s.mods.InterceptStanza(ctx, msg, false)
if err != nil {
switch err {
case nil:
break
case module.ErrInterceptStanzaInterrupted:
return nil // stanza routing interrupted
default:
return err
}
targets, err := s.router.Route(ctx, tst)
Expand All @@ -578,7 +593,7 @@ sendMsg:
BuildMessage(false)
goto sendMsg

case router.ErrNotExistingAccount, router.ErrBlockedSender:
case router.ErrNotExistingAccount:
return s.sendElement(ctx, stanzaerror.E(stanzaerror.ServiceUnavailable, message).Element())

case router.ErrRemoteServerNotFound:
Expand Down
44 changes: 0 additions & 44 deletions c2s/in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,28 +547,6 @@ func TestInC2S_HandleSessionElement(t *testing.T) {
expectedOutput: `<iq from="noelia@localhost/hall" to="ortuman@localhost/yard" type="error" id="iq_1"><ping xmlns="urn:xmpp:ping"/><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>`,
expectedState: inBounded,
},
{
name: "Bounded/RouteIQBlockedSender",
state: inBounded,
flags: fSecured | fCompressed | fAuthenticated | fSessionStarted,
sessionResFn: func() (stravaganza.Element, error) {
iq, _ := stravaganza.NewIQBuilder().
WithAttribute(stravaganza.From, "ortuman@localhost/yard").
WithAttribute(stravaganza.To, "noelia@localhost/hall").
WithAttribute(stravaganza.Type, stravaganza.SetType).
WithAttribute(stravaganza.ID, "iq_1").
WithChild(
stravaganza.NewBuilder("ping").
WithAttribute(stravaganza.Namespace, "urn:xmpp:ping").
Build(),
).
BuildIQ(false)
return iq, nil
},
routeError: router.ErrBlockedSender,
expectedOutput: `<iq from="noelia@localhost/hall" to="ortuman@localhost/yard" type="error" id="iq_1"><ping xmlns="urn:xmpp:ping"/><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>`,
expectedState: inBounded,
},
{
name: "Bounded/RouteIQFailedRemoteConnect",
state: inBounded,
Expand Down Expand Up @@ -644,28 +622,6 @@ func TestInC2S_HandleSessionElement(t *testing.T) {
expectedState: inBounded,
expectRouted: true,
},
{
name: "Bounded/RouteMessageBlockedSender",
state: inBounded,
flags: fSecured | fCompressed | fAuthenticated | fSessionStarted,
sessionResFn: func() (stravaganza.Element, error) {
pr, _ := stravaganza.NewMessageBuilder().
WithAttribute(stravaganza.From, "ortuman@localhost/yard").
WithAttribute(stravaganza.To, "noelia@localhost/hall").
WithAttribute(stravaganza.Type, stravaganza.AvailableType).
WithAttribute(stravaganza.ID, "msg_1").
WithChild(
stravaganza.NewBuilder("body").
WithText("I'll give thee a wind.").
Build(),
).
BuildMessage(false)
return pr, nil
},
routeError: router.ErrBlockedSender,
expectedOutput: `<message from="noelia@localhost/hall" to="ortuman@localhost/yard" type="error" id="msg_1"><body>I&#39;ll give thee a wind.</body><error code="503" type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></message>`,
expectedState: inBounded,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
30 changes: 0 additions & 30 deletions c2s/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ func NewRouter(
}

func (r *c2sRouter) Route(ctx context.Context, stanza stravaganza.Stanza, routingOpts router.RoutingOptions) (targets []jid.JID, err error) {
fromJID := stanza.FromJID()
toJID := stanza.ToJID()

// apply validations
username := stanza.ToJID().Node()
if (routingOpts & router.CheckUserExistence) > 0 {
Expand All @@ -71,11 +68,6 @@ func (r *c2sRouter) Route(ctx context.Context, stanza stravaganza.Stanza, routin
return nil, router.ErrNotExistingAccount
}
}
if (routingOpts & router.ValidateSenderJID) > 0 {
if r.isBlockedJID(ctx, fromJID, toJID.Node()) { // check whether sender JID is blocked
return nil, router.ErrBlockedSender
}
}
// get user available resources
rss, err := r.resMng.GetResources(ctx, username)
if err != nil {
Expand Down Expand Up @@ -193,25 +185,3 @@ func (r *c2sRouter) routeTo(ctx context.Context, stanza stravaganza.Stanza, toRe
}
return r.cluster.Route(ctx, stanza, toRes.JID.Node(), toRes.JID.Resource(), toRes.InstanceID)
}

func (r *c2sRouter) isBlockedJID(ctx context.Context, destJID *jid.JID, username string) bool {
blockList, err := r.rep.FetchBlockListItems(ctx, username)
if err != nil {
log.Errorf("Failed to fetch block list items: %v", err)
return false
}
if len(blockList) == 0 {
return false
}
blockListJIDs := make([]jid.JID, len(blockList))
for i, listItem := range blockList {
j, _ := jid.NewWithString(listItem.JID, true)
blockListJIDs[i] = *j
}
for _, blockedJID := range blockListJIDs {
if blockedJID.Matches(destJID) {
return true
}
}
return false
}
16 changes: 0 additions & 16 deletions c2s/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,6 @@ func (s *routerSuite) TestRouter_NotExistingAccount() {
s.Require().Equal(router.ErrNotExistingAccount, err)
}

func (s *routerSuite) TestRouter_BlockedJID() {
// given
s.repositoryMock.FetchBlockListItemsFunc = func(_ context.Context, _ string) ([]model.BlockListItem, error) {
return []model.BlockListItem{
{Username: "ortuman", JID: "noelia@jackal.im"},
}, nil
}

// when
msg := testMessageStanza()
_, err := s.router.Route(context.Background(), msg, router.ValidateSenderJID)

// then
s.Require().Equal(router.ErrBlockedSender, err)
}

func (s *routerSuite) TestRouter_NotAuthenticated() {
// given
s.repositoryMock.UserExistsFunc = func(_ context.Context, _ string) (bool, error) {
Expand Down
20 changes: 18 additions & 2 deletions module/external/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,24 @@ func (m *ExtModule) InterceptStanza(ctx context.Context, stanza stravaganza.Stan
if err != nil {
return nil, err
}
return stravaganza.NewBuilderFromProto(resp.Stanza).
BuildStanza(false)
if resp.Interrupt {
return nil, module.ErrInterceptStanzaInterrupted
}
// ensure stanza type
switch stanza.(type) {
case *stravaganza.IQ:
return stravaganza.NewBuilderFromProto(resp.Stanza).
BuildIQ(false)
case *stravaganza.Presence:
return stravaganza.NewBuilderFromProto(resp.Stanza).
BuildPresence(false)
case *stravaganza.Message:
return stravaganza.NewBuilderFromProto(resp.Stanza).
BuildMessage(false)
default:
return stravaganza.NewBuilderFromProto(resp.Stanza).
BuildStanza(false)
}
}

// Start starts external module.
Expand Down
Loading

0 comments on commit db009f3

Please sign in to comment.