File: chunk_init.go

package info (click to toggle)
golang-github-pion-sctp 1.8.6-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-backports, sid, trixie
  • size: 888 kB
  • sloc: makefile: 13
file content (135 lines) | stat: -rw-r--r-- 4,680 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package sctp // nolint:dupl

import (
	"errors"
	"fmt"
)

/*
Init represents an SCTP Chunk of type INIT

See chunkInitCommon for the fixed headers

Variable Parameters                  Status     Type Value
-------------------------------------------------------------
IPv4 IP (Note 1)               Optional    5
IPv6 IP (Note 1)               Optional    6
Cookie Preservative                 Optional    9
Reserved for ECN Capable (Note 2)   Optional    32768 (0x8000)
Host Name IP (Note 3)          Optional    11
Supported IP Types (Note 4)    Optional    12
*/
type chunkInit struct {
	chunkHeader
	chunkInitCommon
}

// Init chunk errors
var (
	ErrChunkTypeNotTypeInit          = errors.New("ChunkType is not of type INIT")
	ErrChunkValueNotLongEnough       = errors.New("chunk Value isn't long enough for mandatory parameters exp")
	ErrChunkTypeInitFlagZero         = errors.New("ChunkType of type INIT flags must be all 0")
	ErrChunkTypeInitUnmarshalFailed  = errors.New("failed to unmarshal INIT body")
	ErrChunkTypeInitMarshalFailed    = errors.New("failed marshaling INIT common data")
	ErrChunkTypeInitInitateTagZero   = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0")
	ErrInitInboundStreamRequestZero  = errors.New("INIT ACK inbound stream request must be > 0")
	ErrInitOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0")
	ErrInitAdvertisedReceiver1500    = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500")
)

func (i *chunkInit) unmarshal(raw []byte) error {
	if err := i.chunkHeader.unmarshal(raw); err != nil {
		return err
	}

	if i.typ != ctInit {
		return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotTypeInit, i.typ.String())
	} else if len(i.raw) < initChunkMinLength {
		return fmt.Errorf("%w: %d actual: %d", ErrChunkValueNotLongEnough, initChunkMinLength, len(i.raw))
	}

	// The Chunk Flags field in INIT is reserved, and all bits in it should
	// be set to 0 by the sender and ignored by the receiver.  The sequence
	// of parameters within an INIT can be processed in any order.
	if i.flags != 0 {
		return ErrChunkTypeInitFlagZero
	}

	if err := i.chunkInitCommon.unmarshal(i.raw); err != nil {
		return fmt.Errorf("%w: %v", ErrChunkTypeInitUnmarshalFailed, err)
	}

	return nil
}

func (i *chunkInit) marshal() ([]byte, error) {
	initShared, err := i.chunkInitCommon.marshal()
	if err != nil {
		return nil, fmt.Errorf("%w: %v", ErrChunkTypeInitMarshalFailed, err)
	}

	i.chunkHeader.typ = ctInit
	i.chunkHeader.raw = initShared
	return i.chunkHeader.marshal()
}

func (i *chunkInit) check() (abort bool, err error) {
	// The receiver of the INIT (the responding end) records the value of
	// the Initiate Tag parameter.  This value MUST be placed into the
	// Verification Tag field of every SCTP packet that the receiver of
	// the INIT transmits within this association.
	//
	// The Initiate Tag is allowed to have any value except 0.  See
	// Section 5.3.1 for more on the selection of the tag value.
	//
	// If the value of the Initiate Tag in a received INIT chunk is found
	// to be 0, the receiver MUST treat it as an error and close the
	// association by transmitting an ABORT.
	if i.initiateTag == 0 {
		abort = true
		return abort, ErrChunkTypeInitInitateTagZero
	}

	// Defines the maximum number of streams the sender of this INIT
	// chunk allows the peer end to create in this association.  The
	// value 0 MUST NOT be used.
	//
	// Note: There is no negotiation of the actual number of streams but
	// instead the two endpoints will use the min(requested, offered).
	// See Section 5.1.1 for details.
	//
	// Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
	// the association.
	if i.numInboundStreams == 0 {
		abort = true
		return abort, ErrInitInboundStreamRequestZero
	}

	// Defines the number of outbound streams the sender of this INIT
	// chunk wishes to create in this association.  The value of 0 MUST
	// NOT be used.
	//
	// Note: A receiver of an INIT with the OS value set to 0 SHOULD
	// abort the association.

	if i.numOutboundStreams == 0 {
		abort = true
		return abort, ErrInitOutboundStreamRequestZero
	}

	// An SCTP receiver MUST be able to receive a minimum of 1500 bytes in
	// one SCTP packet.  This means that an SCTP endpoint MUST NOT indicate
	// less than 1500 bytes in its initial a_rwnd sent in the INIT or INIT
	// ACK.
	if i.advertisedReceiverWindowCredit < 1500 {
		abort = true
		return abort, ErrInitAdvertisedReceiver1500
	}

	return false, nil
}

// String makes chunkInit printable
func (i *chunkInit) String() string {
	return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
}