Liu Song’s Projects


~/Projects/mqtt-go

git clone https://code.lsong.org/mqtt-go

Commit

Commit
46babc89c82dd3ae4f45fbffb84ace8b75de1a7c
Author
JB <28275108+[email protected]>
Date
2023-02-25 01:37:54 +0000 +0000
Diffstat
 packets/packets.go | 8 ++--
 packets/tpackets.go | 71 +++++++++++++++++++++++++++++++++++++---------
 server_test.go | 27 +++++++++++++++++

Allow 0 byte usernames if correctly formed (#181)

* Allow 0 byte usernames if correctly formed

* Allow 0 byte usernames if correctly formed


diff --git a/packets/packets.go b/packets/packets.go
index 36125d737c5d9e9a451ece864c340cc1c13fda3c..0e0671706a813c1a38fe93903e74d2680bb12907 100644
--- a/packets/packets.go
+++ b/packets/packets.go
@@ -412,6 +412,10 @@ 		}
 	}
 
 	if pk.Connect.UsernameFlag { // [MQTT-3.1.3-12]
+		if offset >= len(buf) { // we are at the end of the packet
+			return ErrProtocolViolationFlagNoUsername // [MQTT-3.1.2-17]
+		}
+
 		pk.Connect.Username, offset, err = decodeBytes(buf, offset)
 		if err != nil {
 			return ErrMalformedUsername
@@ -449,10 +453,6 @@ 	}
 
 	if len(pk.Connect.Username) > math.MaxUint16 {
 		return ErrProtocolViolationUsernameTooLong
-	}
-
-	if pk.Connect.UsernameFlag && len(pk.Connect.Username) == 0 {
-		return ErrProtocolViolationFlagNoUsername // [MQTT-3.1.2-17]
 	}
 
 	if !pk.Connect.UsernameFlag && len(pk.Connect.Username) > 0 {




diff --git a/packets/tpackets.go b/packets/tpackets.go
index d8bc0cacd8648e4fa8db7b18fd8d4cbfaef16e33..934ce05e169a27adb94fe92a78a93b99b76cb2d9 100644
--- a/packets/tpackets.go
+++ b/packets/tpackets.go
@@ -71,8 +71,8 @@ 	TConnectInvalidPasswordTooLong
 	TConnectInvalidWillFlagNoPayload
 	TConnectInvalidWillFlagQosOutOfRange
 	TConnectInvalidWillSurplusRetain
+	TPublishInvalidTopicAlias
 // TPacketCase contains data for cross-checking the encoding and decoding
-type TPacketCase struct {
 	TConnectSpecInvalidUTF8D800
 	TConnectSpecInvalidUTF8DFFF
 	TConnectSpecInvalidUTF80000
@@ -499,6 +499,43 @@ 					WillQos:          1,
 				},
 			},
 		},
+		{
+			Case:  TConnectZeroByteUsername,
+			Desc:  "username flag but 0 byte username",
+			Group: "decode",
+			RawBytes: []byte{
+				Connect << 4, 23, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				5,     // Protocol Version
+				130,   // Packet Flags
+				0, 30, // Keepalive
+				5,                // length
+				17, 0, 0, 0, 120, // Session Expiry Interval (17)
+				0, 3, // Client ID - MSB+LSB
+				'z', 'e', 'n', // Client ID "zen"
+				0, 0, // Username MSB+LSB
+			},
+			Packet: &Packet{
+				FixedHeader: FixedHeader{
+					Type:      Connect,
+					Remaining: 23,
+				},
+				ProtocolVersion: 5,
+				Connect: ConnectParams{
+					ProtocolName:     []byte("MQTT"),
+					Clean:            true,
+					Keepalive:        30,
+					ClientIdentifier: "zen",
+					Username:         []byte{},
+					UsernameFlag:     true,
+				},
+				Properties: Properties{
+					SessionExpiryInterval:     uint32(120),
+					SessionExpiryIntervalFlag: true,
+				},
+			},
+		},
 
 		// Fail States
 		{
@@ -623,6 +660,24 @@ 				0, 9, // Will Message MSB+LSB
 				'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
 				0, 5, // Username MSB+LSB
 				'm', 'o', 'c',
+			},
+		},
+
+		{
+			Case:      TConnectInvalidFlagNoUsername,
+			Desc:      "username flag with no username bytes",
+			Group:     "decode",
+			FailFirst: ErrProtocolViolationFlagNoUsername,
+			RawBytes: []byte{
+				Connect << 4, 17, // Fixed header
+				0, 4, // Protocol Name - MSB+LSB
+				'M', 'Q', 'T', 'T', // Protocol Name
+				5,     // Protocol Version
+				130,   // Flags
+				0, 20, // Keepalive
+				0,
+				0, 3, // Client ID - MSB+LSB
+				'z', 'e', 'n', // Client ID "zen"
 			},
 		},
 		{
@@ -782,20 +837,6 @@ 					ProtocolName: []byte("MQTT"),
 					ClientIdentifier: func() string {
 						return string(make([]byte, 65536))
 					}(),
-				},
-			},
-		},
-		{
-			Case:   TConnectInvalidFlagNoUsername,
-			Desc:   "has username flag but no username",
-			Group:  "validate",
-			Expect: ErrProtocolViolationFlagNoUsername,
-			Packet: &Packet{
-				FixedHeader:     FixedHeader{Type: Connect},
-				ProtocolVersion: 4,
-				Connect: ConnectParams{
-					ProtocolName: []byte("MQTT"),
-					UsernameFlag: true,
 				},
 			},
 		},




diff --git a/server_test.go b/server_test.go
index d6fdaabfb26ac1ad93acd07cf4f5b2d7aff03362..31905ea17b3df6c9da62fd6908ff300d57d826d6 100644
--- a/server_test.go
+++ b/server_test.go
@@ -748,6 +748,33 @@
 	r.Close()
 }
 
+// See https://github.com/mochi-co/mqtt/issues/178
+func TestServerEstablishConnectionZeroByteUsernameIsValid(t *testing.T) {
+	s := newServer()
+
+	r, w := net.Pipe()
+	o := make(chan error)
+	go func() {
+		o <- s.EstablishConnection("tcp", r)
+	}()
+
+	go func() {
+		w.Write(packets.TPacketData[packets.Connect].Get(packets.TConnectZeroByteUsername).RawBytes)
+		w.Write(packets.TPacketData[packets.Disconnect].Get(packets.TDisconnect).RawBytes)
+	}()
+
+	// receive the connack error
+	go func() {
+		_, err := io.ReadAll(w)
+		require.NoError(t, err)
+	}()
+
+	err := <-o
+	require.NoError(t, err)
+
+	r.Close()
+}
+
 func TestServerEstablishConnectionInvalidConnectAckFailure(t *testing.T) {
 	s := newServer()