chunkheader.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package sctp
  4. import (
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. )
  9. /*
  10. chunkHeader represents a SCTP Chunk header, defined in https://tools.ietf.org/html/rfc4960#section-3.2
  11. The figure below illustrates the field format for the chunks to be
  12. transmitted in the SCTP packet. Each chunk is formatted with a Chunk
  13. Type field, a chunk-specific Flag field, a Chunk Length field, and a
  14. Value field.
  15. 0 1 2 3
  16. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  17. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  18. | Chunk Type | Chunk Flags | Chunk Length |
  19. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  20. | |
  21. | Chunk Value |
  22. | |
  23. +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  24. */
  25. type chunkHeader struct {
  26. typ chunkType
  27. flags byte
  28. raw []byte
  29. }
  30. const (
  31. chunkHeaderSize = 4
  32. )
  33. // SCTP chunk header errors
  34. var (
  35. ErrChunkHeaderTooSmall = errors.New("raw is too small for a SCTP chunk")
  36. ErrChunkHeaderNotEnoughSpace = errors.New("not enough data left in SCTP packet to satisfy requested length")
  37. ErrChunkHeaderPaddingNonZero = errors.New("chunk padding is non-zero at offset")
  38. )
  39. func (c *chunkHeader) unmarshal(raw []byte) error {
  40. if len(raw) < chunkHeaderSize {
  41. return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrChunkHeaderTooSmall, len(raw), chunkHeaderSize)
  42. }
  43. c.typ = chunkType(raw[0])
  44. c.flags = raw[1]
  45. length := binary.BigEndian.Uint16(raw[2:])
  46. // Length includes Chunk header
  47. valueLength := int(length - chunkHeaderSize)
  48. lengthAfterValue := len(raw) - (chunkHeaderSize + valueLength)
  49. if lengthAfterValue < 0 {
  50. return fmt.Errorf("%w: remain %d req %d ", ErrChunkHeaderNotEnoughSpace, valueLength, len(raw)-chunkHeaderSize)
  51. } else if lengthAfterValue < 4 {
  52. // https://tools.ietf.org/html/rfc4960#section-3.2
  53. // The Chunk Length field does not count any chunk padding.
  54. // Chunks (including Type, Length, and Value fields) are padded out
  55. // by the sender with all zero bytes to be a multiple of 4 bytes
  56. // long. This padding MUST NOT be more than 3 bytes in total. The
  57. // Chunk Length value does not include terminating padding of the
  58. // chunk. However, it does include padding of any variable-length
  59. // parameter except the last parameter in the chunk. The receiver
  60. // MUST ignore the padding.
  61. for i := lengthAfterValue; i > 0; i-- {
  62. paddingOffset := chunkHeaderSize + valueLength + (i - 1)
  63. if raw[paddingOffset] != 0 {
  64. return fmt.Errorf("%w: %d ", ErrChunkHeaderPaddingNonZero, paddingOffset)
  65. }
  66. }
  67. }
  68. c.raw = raw[chunkHeaderSize : chunkHeaderSize+valueLength]
  69. return nil
  70. }
  71. func (c *chunkHeader) marshal() ([]byte, error) {
  72. raw := make([]byte, 4+len(c.raw))
  73. raw[0] = uint8(c.typ)
  74. raw[1] = c.flags
  75. binary.BigEndian.PutUint16(raw[2:], uint16(len(c.raw)+chunkHeaderSize))
  76. copy(raw[4:], c.raw)
  77. return raw, nil
  78. }
  79. func (c *chunkHeader) valueLength() int {
  80. return len(c.raw)
  81. }
  82. // String makes chunkHeader printable
  83. func (c chunkHeader) String() string {
  84. return c.typ.String()
  85. }