/* * Copyright (c) 2021 IBM Corp and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * https://www.eclipse.org/legal/epl-2.0/ * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Allan Stockdill-Mander */ package packets import ( "bytes" "fmt" "io" ) // ConnectPacket is an internal representation of the fields of the // Connect MQTT packet type ConnectPacket struct { FixedHeader ProtocolName string ProtocolVersion byte CleanSession bool WillFlag bool WillQos byte WillRetain bool UsernameFlag bool PasswordFlag bool ReservedBit byte Keepalive uint16 ClientIdentifier string WillTopic string WillMessage []byte Username string Password []byte } func (c *ConnectPacket) String() string { var password string if len(c.Password) > 0 { password = "" } return fmt.Sprintf("%s protocolversion: %d protocolname: %s cleansession: %t willflag: %t WillQos: %d WillRetain: %t Usernameflag: %t Passwordflag: %t keepalive: %d clientId: %s willtopic: %s willmessage: %s Username: %s Password: %s", c.FixedHeader, c.ProtocolVersion, c.ProtocolName, c.CleanSession, c.WillFlag, c.WillQos, c.WillRetain, c.UsernameFlag, c.PasswordFlag, c.Keepalive, c.ClientIdentifier, c.WillTopic, c.WillMessage, c.Username, password) } func (c *ConnectPacket) Write(w io.Writer) error { var body bytes.Buffer var err error body.Write(encodeString(c.ProtocolName)) body.WriteByte(c.ProtocolVersion) body.WriteByte(boolToByte(c.CleanSession)<<1 | boolToByte(c.WillFlag)<<2 | c.WillQos<<3 | boolToByte(c.WillRetain)<<5 | boolToByte(c.PasswordFlag)<<6 | boolToByte(c.UsernameFlag)<<7) body.Write(encodeUint16(c.Keepalive)) body.Write(encodeString(c.ClientIdentifier)) if c.WillFlag { body.Write(encodeString(c.WillTopic)) body.Write(encodeBytes(c.WillMessage)) } if c.UsernameFlag { body.Write(encodeString(c.Username)) } if c.PasswordFlag { body.Write(encodeBytes(c.Password)) } c.FixedHeader.RemainingLength = body.Len() packet := c.FixedHeader.pack() packet.Write(body.Bytes()) _, err = packet.WriteTo(w) return err } // Unpack decodes the details of a ControlPacket after the fixed // header has been read func (c *ConnectPacket) Unpack(b io.Reader) error { var err error c.ProtocolName, err = decodeString(b) if err != nil { return err } c.ProtocolVersion, err = decodeByte(b) if err != nil { return err } options, err := decodeByte(b) if err != nil { return err } c.ReservedBit = 1 & options c.CleanSession = 1&(options>>1) > 0 c.WillFlag = 1&(options>>2) > 0 c.WillQos = 3 & (options >> 3) c.WillRetain = 1&(options>>5) > 0 c.PasswordFlag = 1&(options>>6) > 0 c.UsernameFlag = 1&(options>>7) > 0 c.Keepalive, err = decodeUint16(b) if err != nil { return err } c.ClientIdentifier, err = decodeString(b) if err != nil { return err } if c.WillFlag { c.WillTopic, err = decodeString(b) if err != nil { return err } c.WillMessage, err = decodeBytes(b) if err != nil { return err } } if c.UsernameFlag { c.Username, err = decodeString(b) if err != nil { return err } } if c.PasswordFlag { c.Password, err = decodeBytes(b) if err != nil { return err } } return nil } // Validate performs validation of the fields of a Connect packet func (c *ConnectPacket) Validate() byte { if c.PasswordFlag && !c.UsernameFlag { return ErrRefusedBadUsernameOrPassword } if c.ReservedBit != 0 { // Bad reserved bit return ErrProtocolViolation } if (c.ProtocolName == "MQIsdp" && c.ProtocolVersion != 3) || (c.ProtocolName == "MQTT" && c.ProtocolVersion != 4) { // Mismatched or unsupported protocol version return ErrRefusedBadProtocolVersion } if c.ProtocolName != "MQIsdp" && c.ProtocolName != "MQTT" { // Bad protocol name return ErrProtocolViolation } if len(c.ClientIdentifier) > 65535 || len(c.Username) > 65535 || len(c.Password) > 65535 { // Bad size field return ErrProtocolViolation } if len(c.ClientIdentifier) == 0 && !c.CleanSession { // Bad client identifier return ErrRefusedIDRejected } return Accepted } // Details returns a Details struct containing the Qos and // MessageID of this ControlPacket func (c *ConnectPacket) Details() Details { return Details{Qos: 0, MessageID: 0} }