chunkheader.go 3.2 KB

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