Просмотр исходного кода

issue #78 - handle keep-alive messages during login sequence

Mike 1 год назад
Родитель
Сommit
a69f09f54a
3 измененных файлов с 77 добавлено и 30 удалено
  1. 60 30
      server/oscar/auth.go
  2. 3 0
      server/oscar/auth_test.go
  3. 14 0
      wire/frames.go

+ 60 - 30
server/oscar/auth.go

@@ -1,6 +1,7 @@
 package oscar
 
 import (
+	"bytes"
 	"context"
 	"errors"
 	"fmt"
@@ -102,7 +103,7 @@ func (rt AuthServer) handleNewConnection(rwc io.ReadWriteCloser) error {
 		return rt.processFLAPAuth(signonFrame, flapc)
 	}
 
-	return rt.processBUCPAuth(flapc, err)
+	return rt.processBUCPAuth(flapc)
 }
 
 func (rt AuthServer) processFLAPAuth(signonFrame wire.FLAPSignonFrame, flapc *wire.FlapClient) error {
@@ -113,35 +114,64 @@ func (rt AuthServer) processFLAPAuth(signonFrame wire.FLAPSignonFrame, flapc *wi
 	return flapc.SendSignoffFrame(tlv)
 }
 
-func (rt AuthServer) processBUCPAuth(flapc *wire.FlapClient, err error) error {
-	challengeRequest := wire.SNAC_0x17_0x06_BUCPChallengeRequest{}
-	if err := flapc.ReceiveSNAC(&wire.SNACFrame{}, &challengeRequest); err != nil {
-		return err
-	}
-
-	outSNAC, err := rt.BUCPChallenge(challengeRequest, uuid.New)
-	if err != nil {
-		return err
-	}
-	if err := flapc.SendSNAC(outSNAC.Frame, outSNAC.Body); err != nil {
-		return err
-	}
-
-	if outSNAC.Frame.SubGroup == wire.BUCPLoginResponse {
-		screenName, _ := challengeRequest.String(wire.LoginTLVTagsScreenName)
-		rt.Logger.Debug("failed BUCP challenge: user does not exist", "screen_name", screenName)
-		return nil // account does not exist
-	}
-
-	loginRequest := wire.SNAC_0x17_0x02_BUCPLoginRequest{}
-	if err := flapc.ReceiveSNAC(&wire.SNACFrame{}, &loginRequest); err != nil {
-		return err
-	}
+func (rt AuthServer) processBUCPAuth(flapc *wire.FlapClient) error {
+	for {
+		frame, err := flapc.ReceiveFLAP()
+		if err != nil {
+			return err
+		}
 
-	outSNAC, err = rt.BUCPLogin(loginRequest, state.NewStubUser)
-	if err != nil {
-		return err
+		switch frame.FrameType {
+		case wire.FLAPFrameSignoff:
+			rt.Logger.Debug("signed off mid-login")
+			return io.EOF // client disconnected
+		case wire.FLAPFrameKeepAlive:
+			rt.Logger.Debug("received flap keepalive frame")
+		case wire.FLAPFrameData:
+			buf := bytes.NewReader(frame.Payload)
+			fr := wire.SNACFrame{}
+			if err := wire.UnmarshalBE(&fr, buf); err != nil {
+				return err
+			}
+			switch {
+			case fr.FoodGroup == wire.BUCP && fr.SubGroup == wire.BUCPChallengeRequest:
+				challengeRequest := wire.SNAC_0x17_0x06_BUCPChallengeRequest{}
+				if err := wire.UnmarshalBE(&challengeRequest, buf); err != nil {
+					return err
+				}
+				outSNAC, err := rt.BUCPChallenge(challengeRequest, uuid.New)
+				if err != nil {
+					return err
+				}
+				if err := flapc.SendSNAC(outSNAC.Frame, outSNAC.Body); err != nil {
+					return err
+				}
+
+				if outSNAC.Frame.SubGroup == wire.BUCPLoginResponse {
+					screenName, _ := challengeRequest.String(wire.LoginTLVTagsScreenName)
+					rt.Logger.Debug("failed BUCP challenge: user does not exist", "screen_name", screenName)
+					return nil // account does not exist
+				}
+			case fr.FoodGroup == wire.BUCP && fr.SubGroup == wire.BUCPLoginRequest:
+				loginRequest := wire.SNAC_0x17_0x02_BUCPLoginRequest{}
+				if err := wire.UnmarshalBE(&loginRequest, buf); err != nil {
+					return err
+				}
+				outSNAC, err := rt.BUCPLogin(loginRequest, state.NewStubUser)
+				if err != nil {
+					return err
+				}
+
+				return flapc.SendSNAC(outSNAC.Frame, outSNAC.Body)
+			default:
+				rt.Logger.Debug("unexpected SNAC received during login",
+					"foodgroup", wire.FoodGroupName(fr.FoodGroup),
+					"subgroup", wire.SubGroupName(fr.FoodGroup, fr.SubGroup))
+				return io.EOF
+			}
+		default:
+			rt.Logger.Debug("unexpected frame type received during login", "type", frame.FrameType)
+			return io.EOF
+		}
 	}
-
-	return flapc.SendSNAC(outSNAC.Frame, outSNAC.Body)
 }

+ 3 - 0
server/oscar/auth_test.go

@@ -50,6 +50,9 @@ func TestBUCPAuthService_handleNewConnection(t *testing.T) {
 		assert.NoError(t, flapc.ReceiveSNAC(&frame, &wire.SNAC_0x17_0x07_BUCPChallengeResponse{}))
 		assert.Equal(t, wire.SNACFrame{FoodGroup: wire.BUCP, SubGroup: wire.BUCPChallengeResponse}, frame)
 
+		// > send keep alive frame (like BSFlite does mid-login)
+		assert.NoError(t, flapc.SendKeepAliveFrame())
+
 		// > send SNAC_0x17_0x02_BUCPLoginRequest
 		frame = wire.SNACFrame{
 			FoodGroup: wire.BUCP,

+ 14 - 0
wire/frames.go

@@ -199,6 +199,20 @@ func (f *FlapClient) SendDataFrame(payload []byte) error {
 	return nil
 }
 
+func (f *FlapClient) SendKeepAliveFrame() error {
+	flap := FLAPFrame{
+		StartMarker: 42,
+		FrameType:   FLAPFrameKeepAlive,
+		Sequence:    uint16(f.sequence),
+	}
+	if err := MarshalBE(flap, f.w); err != nil {
+		return err
+	}
+
+	f.sequence++
+	return nil
+}
+
 // ReceiveSNAC receives a SNAC message wrapped in a FLAP frame.
 func (f *FlapClient) ReceiveSNAC(frame *SNACFrame, body any) error {
 	flap := FLAPFrame{}