Skip to content

Commit

Permalink
Allow 0 byte usernames if correctly formed (#181)
Browse files Browse the repository at this point in the history
* Allow 0 byte usernames if correctly formed

* Allow 0 byte usernames if correctly formed
  • Loading branch information
mochi-co authored Feb 25, 2023
1 parent 9b7a943 commit 46babc8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 19 deletions.
8 changes: 4 additions & 4 deletions packets/packets.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,10 @@ func (pk *Packet) ConnectDecode(buf []byte) error {
}

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
Expand Down Expand Up @@ -451,10 +455,6 @@ func (pk *Packet) ConnectValidate() Code {
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 {
return ErrProtocolViolationUsernameNoFlag // [MQTT-3.1.2-16]
}
Expand Down
71 changes: 56 additions & 15 deletions packets/tpackets.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const (
TConnectInvalidWillFlagNoPayload
TConnectInvalidWillFlagQosOutOfRange
TConnectInvalidWillSurplusRetain
TConnectNotCleanNoClientID
TConnectZeroByteUsername
TConnectSpecInvalidUTF8D800
TConnectSpecInvalidUTF8DFFF
TConnectSpecInvalidUTF80000
Expand Down Expand Up @@ -498,6 +498,43 @@ var TPacketData = map[byte]TPacketCases{
},
},
},
{
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
{
Expand Down Expand Up @@ -624,6 +661,24 @@ var TPacketData = map[byte]TPacketCases{
'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"
},
},
{
Case: TConnectMalPassword,
Desc: "malformed password",
Expand Down Expand Up @@ -784,20 +839,6 @@ var TPacketData = map[byte]TPacketCases{
},
},
},
{
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,
},
},
},
{
Case: TConnectInvalidUsernameNoFlag,
Desc: "has username but no flag",
Expand Down
27 changes: 27 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,33 @@ func TestServerEstablishConnectionInvalidConnect(t *testing.T) {
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()

Expand Down

0 comments on commit 46babc8

Please sign in to comment.