package packet import ( "errors" "github.com/yosssi/gmq/mqtt" ) // Minimum length of the fixed header of the SUBACK Packet const minLenSUBACKFixedHeader = 2 // Length of the variable header of the SUBACK Packet const lenSUBACKVariableHeader = 2 // Return Code Failure const SUBACKRetFailure byte = 0x80 // Error value var ErrInvalidSUBACKReturnCode = errors.New("invalid SUBACK Return Code") // SUBACK represents a SUBACK Packet. type SUBACK struct { base // PacketID is the Packet Identifier of the variable header. PacketID uint16 // ReturnCodes is the Return Codes of the payload. ReturnCodes []byte } // NewSUBACKFromBytes creates a SUBACK Packet // from the byte data and returns it. func NewSUBACKFromBytes(fixedHeader FixedHeader, remaining []byte) (Packet, error) { // Validate the byte data. if err := validateSUBACKBytes(fixedHeader, remaining); err != nil { return nil, err } // Extract the variable header. variableHeader := remaining[0:lenSUBACKVariableHeader] // Extract the payload. payload := remaining[lenSUBACKVariableHeader:] // Decode the Packet Identifier. // No error occur because of the precedent validation and // the returned error is not be taken care of. packetID, _ := decodeUint16(variableHeader) // Create a PUBACK Packet. p := &SUBACK{ PacketID: packetID, ReturnCodes: payload, } // Set the fixed header to the Packet. p.fixedHeader = fixedHeader // Set the variable header to the Packet. p.variableHeader = variableHeader // Set the payload to the Packet. p.payload = payload // Return the Packet. return p, nil } // validateSUBACKBytes validates the fixed header and the remaining. func validateSUBACKBytes(fixedHeader FixedHeader, remaining []byte) error { // Extract the MQTT Control Packet type. ptype, err := fixedHeader.ptype() if err != nil { return err } // Check the length of the fixed header. if len(fixedHeader) < minLenSUBACKFixedHeader { return ErrInvalidFixedHeaderLen } // Check the MQTT Control Packet type. if ptype != TypeSUBACK { return ErrInvalidPacketType } // Check the reserved bits of the fixed header. if fixedHeader[0]<<4 != 0x00 { return ErrInvalidFixedHeader } // Check the length of the remaining. if len(remaining) < lenSUBACKVariableHeader+1 { return ErrInvalidRemainingLen } // Extract the Packet Identifier. packetID, _ := decodeUint16(remaining[0:lenSUBACKVariableHeader]) // Check the Packet Identifier. if packetID == 0 { return ErrInvalidPacketID } // Check each Return Code. for _, b := range remaining[lenSUBACKVariableHeader:] { if !mqtt.ValidQoS(b) && b != SUBACKRetFailure { return ErrInvalidSUBACKReturnCode } } return nil }