tlv.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package wire
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. )
  7. // TLV represents dynamically typed data in the OSCAR protocol. Each message
  8. // consists of a tag (or key) and a blob value. TLVs are typically grouped
  9. // together in arrays.
  10. type TLV struct {
  11. Tag uint16
  12. Value []byte `oscar:"len_prefix=uint16"`
  13. }
  14. // NewTLVBE creates a new TLV. Values are marshalled in big-endian order.
  15. func NewTLVBE(tag uint16, val any) TLV {
  16. return newTLV(tag, val, binary.BigEndian)
  17. }
  18. // NewTLVLE creates a new TLV. Values are marshalled in little-endian order.
  19. func NewTLVLE(tag uint16, val any) TLV {
  20. return newTLV(tag, val, binary.LittleEndian)
  21. }
  22. func newTLV(tag uint16, val any, order binary.ByteOrder) TLV {
  23. t := TLV{
  24. Tag: tag,
  25. }
  26. if _, ok := val.([]byte); ok {
  27. t.Value = val.([]byte)
  28. } else {
  29. buf := &bytes.Buffer{}
  30. switch order {
  31. case binary.BigEndian:
  32. if err := MarshalBE(val, buf); err != nil {
  33. panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
  34. }
  35. case binary.LittleEndian:
  36. if err := MarshalLE(val, buf); err != nil {
  37. panic(fmt.Sprintf("unable to create TLV: %s", err.Error()))
  38. }
  39. }
  40. t.Value = buf.Bytes()
  41. }
  42. return t
  43. }
  44. // TLVRestBlock is a type of TLV array that does not have any length
  45. // information encoded in the blob. This typically means that a given offset in
  46. // the SNAC payload, the TLV occupies the "rest" of the payload.
  47. type TLVRestBlock struct {
  48. TLVList
  49. }
  50. // TLVBlock is a type of TLV array that has the TLV element count encoded as a
  51. // 2-byte value at the beginning of the encoded blob.
  52. type TLVBlock struct {
  53. TLVList `oscar:"count_prefix=uint16"`
  54. }
  55. // TLVLBlock is a type of TLV array that has the TLV blob byte-length encoded
  56. // as a 2-byte value at the beginning of the encoded blob.
  57. type TLVLBlock struct {
  58. TLVList `oscar:"len_prefix=uint16"`
  59. }
  60. // TLVList is a list of TLV elements. It provides methods to append and access
  61. // TLVs in the array. It provides methods that decode the data blob into the
  62. // appropriate type at runtime. The caller assumes the TLV data type at runtime
  63. // based on the protocol specification. These methods are not safe for
  64. // read-write access by multiple goroutines.
  65. type TLVList []TLV
  66. // Append adds a TLV to the end of the TLV list.
  67. func (s *TLVList) Append(tlv TLV) {
  68. *s = append(*s, tlv)
  69. }
  70. // AppendList adds a TLV list to the end of the TLV list.
  71. func (s *TLVList) AppendList(tlvs []TLV) {
  72. *s = append(*s, tlvs...)
  73. }
  74. // HasTag indicates if a TLV list has a tag.
  75. func (s *TLVList) HasTag(tag uint16) bool {
  76. for _, tlv := range *s {
  77. if tag == tlv.Tag {
  78. return true
  79. }
  80. }
  81. return false
  82. }
  83. // Replace updates the values of TLVs in the list with the same tag as new. If
  84. // no matching tag is found, the list remains unchanged.
  85. func (s *TLVList) Replace(new TLV) {
  86. for i, old := range *s {
  87. if old.Tag == new.Tag {
  88. (*s)[i].Value = new.Value
  89. }
  90. }
  91. }
  92. // String retrieves the string value associated with the specified tag from the
  93. // TLVList.
  94. //
  95. // If the specified tag is found, the function returns the associated string
  96. // value and true. If the tag is not found, the function returns an empty
  97. // string and false.
  98. func (s *TLVList) String(tag uint16) (string, bool) {
  99. for _, tlv := range *s {
  100. if tag == tlv.Tag {
  101. return string(tlv.Value), true
  102. }
  103. }
  104. return "", false
  105. }
  106. // ICQString retrieves the ICQ string value associated with the specified tag
  107. // from the TLVList.
  108. //
  109. // An ICQ string is a string that is prefixed with its length and ends with a
  110. // null terminator.
  111. //
  112. // If the specified tag is found, the function returns the extracted string
  113. // value and true. If the tag is not found or the string is malformed, the
  114. // function returns an empty string and false.
  115. func (s *TLVList) ICQString(tag uint16) (string, bool) {
  116. // Find the TLV entry with the specified tag
  117. for _, tlv := range *s {
  118. if tag != tlv.Tag {
  119. continue
  120. }
  121. // Ensure the value is long enough to contain a valid length prefix and value
  122. if len(tlv.Value) < 3 {
  123. break
  124. }
  125. // Extract the length prefix (first 2 bytes) as a uint16
  126. expectedLength := binary.LittleEndian.Uint16(tlv.Value[0:2])
  127. // Extract the actual string value, excluding the length prefix
  128. value := tlv.Value[2:]
  129. // Check if the length matches the value length (including the null terminator)
  130. if int(expectedLength) != len(value) {
  131. break
  132. }
  133. // Remove the null terminator
  134. return string(value[:len(value)-1]), true
  135. }
  136. // Tag not found
  137. return "", false
  138. }
  139. // Bytes retrieves the byte payload associated with the specified tag from the
  140. // TLVList.
  141. //
  142. // If the specified tag is found, the function returns the associated byte
  143. // slice and true. If the tag is not found, the function returns nil and false.
  144. func (s *TLVList) Bytes(tag uint16) ([]byte, bool) {
  145. for _, tlv := range *s {
  146. if tag == tlv.Tag {
  147. return tlv.Value, true
  148. }
  149. }
  150. return nil, false
  151. }
  152. // Uint8 retrieves a byte value from the TLVList associated with the specified
  153. // tag.
  154. //
  155. // If the specified tag is found, the function returns the associated value
  156. // as a uint8 and true. If the tag is not found, the function returns 0 and
  157. // false.
  158. func (s *TLVList) Uint8(tag uint16) (uint8, bool) {
  159. for _, tlv := range *s {
  160. if tag == tlv.Tag {
  161. if len(tlv.Value) > 0 {
  162. return tlv.Value[0], true
  163. }
  164. }
  165. }
  166. return 0, false
  167. }
  168. // Uint16BE retrieves a 16-bit unsigned integer value from the TLVList
  169. // associated with the specified tag, interpreting the bytes in big-endian
  170. // format.
  171. //
  172. // If the specified tag is found, the function returns the associated value
  173. // as a uint16 and true. If the tag is not found, the function returns 0 and
  174. // false.
  175. func (s *TLVList) Uint16BE(tag uint16) (uint16, bool) {
  176. return s.uint16(tag, binary.BigEndian)
  177. }
  178. // Uint16LE retrieves a 16-bit unsigned integer value from the TLVList
  179. // associated with the specified tag, interpreting the bytes in little-endian
  180. // format.
  181. //
  182. // If the specified tag is found, the function returns the associated value
  183. // as a uint16 and true. If the tag is not found, the function returns 0 and
  184. // false.
  185. func (s *TLVList) Uint16LE(tag uint16) (uint16, bool) {
  186. return s.uint16(tag, binary.LittleEndian)
  187. }
  188. func (s *TLVList) uint16(tag uint16, order binary.ByteOrder) (uint16, bool) {
  189. for _, tlv := range *s {
  190. if tag == tlv.Tag {
  191. return order.Uint16(tlv.Value), true
  192. }
  193. }
  194. return 0, false
  195. }
  196. // Uint32BE retrieves a 32-bit unsigned integer value from the TLVList
  197. // associated with the specified tag, interpreting the bytes in big-endian format.
  198. //
  199. // If the specified tag is found, the function returns the associated value
  200. // as a uint32 and true. If the tag is not found, the function returns 0 and false.
  201. func (s *TLVList) Uint32BE(tag uint16) (uint32, bool) {
  202. return s.uint32(tag, binary.BigEndian)
  203. }
  204. // Uint32LE retrieves a 32-bit unsigned integer value from the TLVList
  205. // associated with the specified tag, interpreting the bytes in little-endian format.
  206. //
  207. // If the specified tag is found, the function returns the associated value
  208. // as a uint32 and true. If the tag is not found, the function returns 0 and false.
  209. func (s *TLVList) Uint32LE(tag uint16) (uint32, bool) {
  210. return s.uint32(tag, binary.LittleEndian)
  211. }
  212. func (s *TLVList) uint32(tag uint16, order binary.ByteOrder) (uint32, bool) {
  213. for _, tlv := range *s {
  214. if tag == tlv.Tag {
  215. return order.Uint32(tlv.Value), true
  216. }
  217. }
  218. return 0, false
  219. }