You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
5.5 KiB

  1. package packet
  2. import (
  3. "errors"
  4. "github.com/yosssi/gmq/mqtt"
  5. )
  6. // Maximum Remaining Length
  7. const maxRemainingLength = 268435455
  8. // Minimum length of the fixed header of the PUBLISH Packet
  9. const minLenPUBLISHFixedHeader = 2
  10. // Minimum length of the variable header of the PUBLISH Packet
  11. const minLenPUBLISHVariableHeader = 2
  12. // Error value
  13. var ErrInvalidPacketID = errors.New("invalid Packet Identifier")
  14. // PUBLISH represents a PUBLISH Packet.
  15. type PUBLISH struct {
  16. base
  17. // dup is the DUP flag of the fixed header.
  18. DUP bool
  19. // qos is the QoS of the fixed header.
  20. QoS byte
  21. // retain is the Retain of the fixed header.
  22. retain bool
  23. // topicName is the Topic Name of the varible header.
  24. TopicName []byte
  25. // packetID is the Packet Identifier of the variable header.
  26. PacketID uint16
  27. // message is the Application Message of the payload.
  28. Message []byte
  29. }
  30. // setFixedHeader sets the fixed header to the Packet.
  31. func (p *PUBLISH) setFixedHeader() {
  32. // Define the first byte of the fixed header.
  33. b := TypePUBLISH << 4
  34. // Set 1 to the Bit 3 if the DUP flag is true.
  35. if p.DUP {
  36. b |= 0x08
  37. }
  38. // Set the value of the Will QoS to the Bit 2 and 1.
  39. b |= p.QoS << 1
  40. // Set 1 to the Bit 0 if the Retain is true.
  41. if p.retain {
  42. b |= 0x01
  43. }
  44. // Append the first byte to the fixed header.
  45. p.fixedHeader = append(p.fixedHeader, b)
  46. // Append the Remaining Length to the fixed header.
  47. p.appendRemainingLength()
  48. }
  49. // setVariableHeader sets the variable header to the Packet.
  50. func (p *PUBLISH) setVariableHeader() {
  51. // Append the Topic Name to the variable header.
  52. p.variableHeader = appendLenStr(p.variableHeader, p.TopicName)
  53. if p.QoS != mqtt.QoS0 {
  54. // Append the Packet Identifier to the variable header.
  55. p.variableHeader = append(p.variableHeader, encodeUint16(p.PacketID)...)
  56. }
  57. }
  58. // setPayload sets the payload to the Packet.
  59. func (p *PUBLISH) setPayload() {
  60. p.payload = p.Message
  61. }
  62. // NewPUBLISH creates and returns a PUBLISH Packet.
  63. func NewPUBLISH(opts *PUBLISHOptions) (Packet, error) {
  64. // Initialize the options.
  65. if opts == nil {
  66. opts = &PUBLISHOptions{}
  67. }
  68. // Validate the options.
  69. if err := opts.validate(); err != nil {
  70. return nil, err
  71. }
  72. // Create a PUBLISH Packet.
  73. p := &PUBLISH{
  74. DUP: opts.DUP,
  75. QoS: opts.QoS,
  76. retain: opts.Retain,
  77. TopicName: opts.TopicName,
  78. PacketID: opts.PacketID,
  79. Message: opts.Message,
  80. }
  81. // Set the variable header to the Packet.
  82. p.setVariableHeader()
  83. // Set the payload to the Packet.
  84. p.setPayload()
  85. // Set the Fixed header to the Packet.
  86. p.setFixedHeader()
  87. // Return the Packet.
  88. return p, nil
  89. }
  90. // NewPUBLISHFromBytes creates the PUBLISH Packet
  91. // from the byte data and returns it.
  92. func NewPUBLISHFromBytes(fixedHeader FixedHeader, remaining []byte) (Packet, error) {
  93. // Validate the byte data.
  94. if err := validatePUBLISHBytes(fixedHeader, remaining); err != nil {
  95. return nil, err
  96. }
  97. // Get the first byte from the fixedHeader.
  98. b := fixedHeader[0]
  99. // Create a PUBLISH Packet.
  100. p := &PUBLISH{
  101. DUP: b&0x08 == 0x08,
  102. QoS: b & 0x06 >> 1,
  103. retain: b&0x01 == 0x01,
  104. }
  105. // Set the fixed header to the Packet.
  106. p.fixedHeader = fixedHeader
  107. // Extract the length of the Topic Name.
  108. lenTopicName, _ := decodeUint16(remaining[0:2])
  109. // Calculate the length of the variable header.
  110. var lenVariableHeader int
  111. if p.QoS == mqtt.QoS0 {
  112. lenVariableHeader = 2 + int(lenTopicName)
  113. } else {
  114. lenVariableHeader = 2 + int(lenTopicName) + 2
  115. }
  116. // Set the variable header to the Packet.
  117. p.variableHeader = remaining[:lenVariableHeader]
  118. // Set the payload to the Packet.
  119. p.payload = remaining[lenVariableHeader:]
  120. // Set the Topic Name to the Packet.
  121. p.TopicName = remaining[2 : 2+lenTopicName]
  122. // Extract the Packet Identifier.
  123. var packetID uint16
  124. if p.QoS != mqtt.QoS0 {
  125. packetID, _ = decodeUint16(remaining[2+lenTopicName : 2+lenTopicName+2])
  126. }
  127. // Set the Packet Identifier to the Packet.
  128. p.PacketID = packetID
  129. // Set the Application Message to the Packet.
  130. p.Message = p.payload
  131. // Return the Packet.
  132. return p, nil
  133. }
  134. // validatePUBLISHBytes validates the fixed header and the variable header.
  135. func validatePUBLISHBytes(fixedHeader FixedHeader, remaining []byte) error {
  136. // Extract the MQTT Control Packet type.
  137. ptype, err := fixedHeader.ptype()
  138. if err != nil {
  139. return err
  140. }
  141. // Check the length of the fixed header.
  142. if len(fixedHeader) < minLenPUBLISHFixedHeader {
  143. return ErrInvalidFixedHeaderLen
  144. }
  145. // Check the MQTT Control Packet type.
  146. if ptype != TypePUBLISH {
  147. return ErrInvalidPacketType
  148. }
  149. // Get the QoS.
  150. qos := (fixedHeader[0] & 0x06) >> 1
  151. // Check the QoS.
  152. if !mqtt.ValidQoS(qos) {
  153. return ErrInvalidQoS
  154. }
  155. // Check the length of the remaining.
  156. if l := len(remaining); l < minLenPUBLISHVariableHeader || l > maxRemainingLength {
  157. return ErrInvalidRemainingLen
  158. }
  159. // Extract the length of the Topic Name.
  160. lenTopicName, _ := decodeUint16(remaining[0:2])
  161. // Calculate the length of the variable header.
  162. var lenVariableHeader int
  163. if qos == mqtt.QoS0 {
  164. lenVariableHeader = 2 + int(lenTopicName)
  165. } else {
  166. lenVariableHeader = 2 + int(lenTopicName) + 2
  167. }
  168. // Check the length of the remaining.
  169. if len(remaining) < lenVariableHeader {
  170. return ErrInvalidRemainingLength
  171. }
  172. // End the validation if the QoS equals to QoS 0.
  173. if qos == mqtt.QoS0 {
  174. return nil
  175. }
  176. // Extract the Packet Identifier.
  177. packetID, _ := decodeUint16(remaining[2+int(lenTopicName) : 2+int(lenTopicName)+2])
  178. // Check the Packet Identifier.
  179. if packetID == 0 {
  180. return ErrInvalidPacketID
  181. }
  182. return nil
  183. }