直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
28
server/vendor/github.com/pion/sctp/.gitignore
generated
vendored
Normal file
28
server/vendor/github.com/pion/sctp/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
### JetBrains IDE ###
|
||||
#####################
|
||||
.idea/
|
||||
|
||||
### Emacs Temporary Files ###
|
||||
#############################
|
||||
*~
|
||||
|
||||
### Folders ###
|
||||
###############
|
||||
bin/
|
||||
vendor/
|
||||
node_modules/
|
||||
|
||||
### Files ###
|
||||
#############
|
||||
*.ivf
|
||||
*.ogg
|
||||
tags
|
||||
cover.out
|
||||
*.sw[poe]
|
||||
*.wasm
|
||||
examples/sfu-ws/cert.pem
|
||||
examples/sfu-ws/key.pem
|
||||
wasm_exec.js
|
||||
125
server/vendor/github.com/pion/sctp/.golangci.yml
generated
vendored
Normal file
125
server/vendor/github.com/pion/sctp/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable:
|
||||
- shadow
|
||||
misspell:
|
||||
locale: US
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
forbidigo:
|
||||
forbid:
|
||||
- ^fmt.Print(f|ln)?$
|
||||
- ^log.(Panic|Fatal|Print)(f|ln)?$
|
||||
- ^os.Exit$
|
||||
- ^panic$
|
||||
- ^print(ln)?$
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||
- bidichk # Checks for dangerous unicode character sequences
|
||||
- bodyclose # checks whether HTTP response body is closed successfully
|
||||
- contextcheck # check the function whether use a non-inherited context
|
||||
- decorder # check declaration order and count of types, constants, variables and functions
|
||||
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||
- dupl # Tool for code clone detection
|
||||
- durationcheck # check for two durations multiplied together
|
||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
|
||||
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
|
||||
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- forbidigo # Forbids identifiers
|
||||
- forcetypeassert # finds forced type assertions
|
||||
- gci # Gci control golang package import order and make it always deterministic.
|
||||
- gochecknoglobals # Checks that no globals are present in Go code
|
||||
- gochecknoinits # Checks that no init functions are present in Go code
|
||||
- gocognit # Computes and checks the cognitive complexity of functions
|
||||
- goconst # Finds repeated strings that could be replaced by a constant
|
||||
- gocritic # The most opinionated Go source code linter
|
||||
- godox # Tool for detection of FIXME, TODO and other comment keywords
|
||||
- goerr113 # Golang linter to check the errors handling expressions
|
||||
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
|
||||
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
|
||||
- goheader # Checks is file header matches to pattern
|
||||
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
|
||||
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
|
||||
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
|
||||
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||
- gosec # Inspects source code for security problems
|
||||
- gosimple # Linter for Go source code that specializes in simplifying a code
|
||||
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- grouper # An analyzer to analyze expression groups.
|
||||
- importas # Enforces consistent import aliases
|
||||
- ineffassign # Detects when assignments to existing variables are not used
|
||||
- misspell # Finds commonly misspelled English words in comments
|
||||
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
|
||||
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
|
||||
- noctx # noctx finds sending http request without context.Context
|
||||
- predeclared # find code that shadows one of Go's predeclared identifiers
|
||||
- revive # golint replacement, finds style mistakes
|
||||
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||
- stylecheck # Stylecheck is a replacement for golint
|
||||
- tagliatelle # Checks the struct tags.
|
||||
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
|
||||
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
|
||||
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
|
||||
- unconvert # Remove unnecessary type conversions
|
||||
- unparam # Reports unused function parameters
|
||||
- unused # Checks Go code for unused constants, variables, functions and types
|
||||
- wastedassign # wastedassign finds wasted assignment statements
|
||||
- whitespace # Tool for detection of leading and trailing whitespace
|
||||
disable:
|
||||
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||
- containedctx # containedctx is a linter that detects struct contained context.Context field
|
||||
- cyclop # checks function and package cyclomatic complexity
|
||||
- exhaustivestruct # Checks if all struct's fields are initialized
|
||||
- funlen # Tool for detection of long functions
|
||||
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||
- godot # Check if comments end in a period
|
||||
- gomnd # An analyzer to detect magic numbers.
|
||||
- ifshort # Checks that your code uses short syntax for if-statements whenever possible
|
||||
- ireturn # Accept Interfaces, Return Concrete Types
|
||||
- lll # Reports long lines
|
||||
- maintidx # maintidx measures the maintainability index of each function.
|
||||
- makezero # Finds slice declarations with non-zero initial length
|
||||
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
|
||||
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||
- nestif # Reports deeply nested if statements
|
||||
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
|
||||
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||
- promlinter # Check Prometheus metrics naming via promlint
|
||||
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
|
||||
- testpackage # linter that makes you use a separate _test package
|
||||
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
|
||||
- varnamelen # checks that the length of a variable's name matches its scope
|
||||
- wrapcheck # Checks that errors returned from external packages are wrapped
|
||||
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude-dirs-use-default: false
|
||||
exclude-rules:
|
||||
# Allow complex tests and examples, better to be self contained
|
||||
- path: (examples|main\.go|_test\.go)
|
||||
linters:
|
||||
- forbidigo
|
||||
- gocognit
|
||||
|
||||
# Allow forbidden identifiers in CLI commands
|
||||
- path: cmd
|
||||
linters:
|
||||
- forbidigo
|
||||
5
server/vendor/github.com/pion/sctp/.goreleaser.yml
generated
vendored
Normal file
5
server/vendor/github.com/pion/sctp/.goreleaser.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
builds:
|
||||
- skip: true
|
||||
9
server/vendor/github.com/pion/sctp/LICENSE
generated
vendored
Normal file
9
server/vendor/github.com/pion/sctp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 The Pion community <https://pion.ly>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
34
server/vendor/github.com/pion/sctp/README.md
generated
vendored
Normal file
34
server/vendor/github.com/pion/sctp/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
Pion SCTP
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of SCTP</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-sctp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SCTP"></a>
|
||||
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/sctp/test.yaml">
|
||||
<a href="https://pkg.go.dev/github.com/pion/sctp"><img src="https://pkg.go.dev/badge/github.com/pion/sctp.svg" alt="Go Reference"></a>
|
||||
<a href="https://codecov.io/gh/pion/sctp"><img src="https://codecov.io/gh/pion/sctp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/sctp"><img src="https://goreportcard.com/badge/github.com/pion/sctp" alt="Go Report Card"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
### Roadmap
|
||||
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||
|
||||
### Community
|
||||
Pion has an active community on the [Slack](https://pion.ly/slack).
|
||||
|
||||
Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
|
||||
|
||||
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||
|
||||
### Contributing
|
||||
Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
104
server/vendor/github.com/pion/sctp/ack_timer.go
generated
vendored
Normal file
104
server/vendor/github.com/pion/sctp/ack_timer.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ackInterval time.Duration = 200 * time.Millisecond
|
||||
)
|
||||
|
||||
// ackTimerObserver is the inteface to an ack timer observer.
|
||||
type ackTimerObserver interface {
|
||||
onAckTimeout()
|
||||
}
|
||||
|
||||
type ackTimerState uint8
|
||||
|
||||
const (
|
||||
ackTimerStopped ackTimerState = iota
|
||||
ackTimerStarted
|
||||
ackTimerClosed
|
||||
)
|
||||
|
||||
// ackTimer provides the retnransmission timer conforms with RFC 4960 Sec 6.3.1
|
||||
type ackTimer struct {
|
||||
timer *time.Timer
|
||||
observer ackTimerObserver
|
||||
mutex sync.Mutex
|
||||
state ackTimerState
|
||||
pending uint8
|
||||
}
|
||||
|
||||
// newAckTimer creates a new acknowledgement timer used to enable delayed ack.
|
||||
func newAckTimer(observer ackTimerObserver) *ackTimer {
|
||||
t := &ackTimer{observer: observer}
|
||||
t.timer = time.AfterFunc(math.MaxInt64, t.timeout)
|
||||
t.timer.Stop()
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *ackTimer) timeout() {
|
||||
t.mutex.Lock()
|
||||
if t.pending--; t.pending == 0 && t.state == ackTimerStarted {
|
||||
t.state = ackTimerStopped
|
||||
defer t.observer.onAckTimeout()
|
||||
}
|
||||
t.mutex.Unlock()
|
||||
}
|
||||
|
||||
// start starts the timer.
|
||||
func (t *ackTimer) start() bool {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
// this timer is already closed or already running
|
||||
if t.state != ackTimerStopped {
|
||||
return false
|
||||
}
|
||||
|
||||
t.state = ackTimerStarted
|
||||
t.pending++
|
||||
t.timer.Reset(ackInterval)
|
||||
return true
|
||||
}
|
||||
|
||||
// stops the timer. this is similar to stop() but subsequent start() call
|
||||
// will fail (the timer is no longer usable)
|
||||
func (t *ackTimer) stop() {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
if t.state == ackTimerStarted {
|
||||
if t.timer.Stop() {
|
||||
t.pending--
|
||||
}
|
||||
t.state = ackTimerStopped
|
||||
}
|
||||
}
|
||||
|
||||
// closes the timer. this is similar to stop() but subsequent start() call
|
||||
// will fail (the timer is no longer usable)
|
||||
func (t *ackTimer) close() {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
if t.state == ackTimerStarted && t.timer.Stop() {
|
||||
t.pending--
|
||||
}
|
||||
t.state = ackTimerClosed
|
||||
}
|
||||
|
||||
// isRunning tests if the timer is running.
|
||||
// Debug purpose only
|
||||
func (t *ackTimer) isRunning() bool {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
return t.state == ackTimerStarted
|
||||
}
|
||||
2747
server/vendor/github.com/pion/sctp/association.go
generated
vendored
Normal file
2747
server/vendor/github.com/pion/sctp/association.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
94
server/vendor/github.com/pion/sctp/association_stats.go
generated
vendored
Normal file
94
server/vendor/github.com/pion/sctp/association_stats.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type associationStats struct {
|
||||
nPacketsReceived uint64
|
||||
nPacketsSent uint64
|
||||
nDATAs uint64
|
||||
nSACKsReceived uint64
|
||||
nSACKsSent uint64
|
||||
nT3Timeouts uint64
|
||||
nAckTimeouts uint64
|
||||
nFastRetrans uint64
|
||||
}
|
||||
|
||||
func (s *associationStats) incPacketsReceived() {
|
||||
atomic.AddUint64(&s.nPacketsReceived, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumPacketsReceived() uint64 {
|
||||
return atomic.LoadUint64(&s.nPacketsReceived)
|
||||
}
|
||||
|
||||
func (s *associationStats) incPacketsSent() {
|
||||
atomic.AddUint64(&s.nPacketsSent, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumPacketsSent() uint64 {
|
||||
return atomic.LoadUint64(&s.nPacketsSent)
|
||||
}
|
||||
|
||||
func (s *associationStats) incDATAs() {
|
||||
atomic.AddUint64(&s.nDATAs, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumDATAs() uint64 {
|
||||
return atomic.LoadUint64(&s.nDATAs)
|
||||
}
|
||||
|
||||
func (s *associationStats) incSACKsReceived() {
|
||||
atomic.AddUint64(&s.nSACKsReceived, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumSACKsReceived() uint64 {
|
||||
return atomic.LoadUint64(&s.nSACKsReceived)
|
||||
}
|
||||
|
||||
func (s *associationStats) incSACKsSent() {
|
||||
atomic.AddUint64(&s.nSACKsSent, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumSACKsSent() uint64 {
|
||||
return atomic.LoadUint64(&s.nSACKsSent)
|
||||
}
|
||||
|
||||
func (s *associationStats) incT3Timeouts() {
|
||||
atomic.AddUint64(&s.nT3Timeouts, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumT3Timeouts() uint64 {
|
||||
return atomic.LoadUint64(&s.nT3Timeouts)
|
||||
}
|
||||
|
||||
func (s *associationStats) incAckTimeouts() {
|
||||
atomic.AddUint64(&s.nAckTimeouts, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumAckTimeouts() uint64 {
|
||||
return atomic.LoadUint64(&s.nAckTimeouts)
|
||||
}
|
||||
|
||||
func (s *associationStats) incFastRetrans() {
|
||||
atomic.AddUint64(&s.nFastRetrans, 1)
|
||||
}
|
||||
|
||||
func (s *associationStats) getNumFastRetrans() uint64 {
|
||||
return atomic.LoadUint64(&s.nFastRetrans)
|
||||
}
|
||||
|
||||
func (s *associationStats) reset() {
|
||||
atomic.StoreUint64(&s.nPacketsReceived, 0)
|
||||
atomic.StoreUint64(&s.nPacketsSent, 0)
|
||||
atomic.StoreUint64(&s.nDATAs, 0)
|
||||
atomic.StoreUint64(&s.nSACKsReceived, 0)
|
||||
atomic.StoreUint64(&s.nSACKsSent, 0)
|
||||
atomic.StoreUint64(&s.nT3Timeouts, 0)
|
||||
atomic.StoreUint64(&s.nAckTimeouts, 0)
|
||||
atomic.StoreUint64(&s.nFastRetrans, 0)
|
||||
}
|
||||
12
server/vendor/github.com/pion/sctp/chunk.go
generated
vendored
Normal file
12
server/vendor/github.com/pion/sctp/chunk.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type chunk interface {
|
||||
unmarshal(raw []byte) error
|
||||
marshal() ([]byte, error)
|
||||
check() (bool, error)
|
||||
|
||||
valueLength() int
|
||||
}
|
||||
96
server/vendor/github.com/pion/sctp/chunk_abort.go
generated
vendored
Normal file
96
server/vendor/github.com/pion/sctp/chunk_abort.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp // nolint:dupl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Abort represents an SCTP Chunk of type ABORT
|
||||
|
||||
The ABORT chunk is sent to the peer of an association to close the
|
||||
association. The ABORT chunk may contain Cause Parameters to inform
|
||||
the receiver about the reason of the abort. DATA chunks MUST NOT be
|
||||
bundled with ABORT. Control chunks (except for INIT, INIT ACK, and
|
||||
SHUTDOWN COMPLETE) MAY be bundled with an ABORT, but they MUST be
|
||||
placed before the ABORT in the SCTP packet or they will be ignored by
|
||||
the receiver.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 6 |Reserved |T| Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| zero or more Error Causes |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkAbort struct {
|
||||
chunkHeader
|
||||
errorCauses []errorCause
|
||||
}
|
||||
|
||||
// Abort chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotAbort = errors.New("ChunkType is not of type ABORT")
|
||||
ErrBuildAbortChunkFailed = errors.New("failed build Abort Chunk")
|
||||
)
|
||||
|
||||
func (a *chunkAbort) unmarshal(raw []byte) error {
|
||||
if err := a.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.typ != ctAbort {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotAbort, a.typ.String())
|
||||
}
|
||||
|
||||
offset := chunkHeaderSize
|
||||
for {
|
||||
if len(raw)-offset < 4 {
|
||||
break
|
||||
}
|
||||
|
||||
e, err := buildErrorCause(raw[offset:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrBuildAbortChunkFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
offset += int(e.length())
|
||||
a.errorCauses = append(a.errorCauses, e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *chunkAbort) marshal() ([]byte, error) {
|
||||
a.chunkHeader.typ = ctAbort
|
||||
a.flags = 0x00
|
||||
a.raw = []byte{}
|
||||
for _, ec := range a.errorCauses {
|
||||
raw, err := ec.marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.raw = append(a.raw, raw...)
|
||||
}
|
||||
return a.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (a *chunkAbort) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkAbort printable
|
||||
func (a *chunkAbort) String() string {
|
||||
res := a.chunkHeader.String()
|
||||
|
||||
for _, cause := range a.errorCauses {
|
||||
res += fmt.Sprintf("\n - %s", cause)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
53
server/vendor/github.com/pion/sctp/chunk_cookie_ack.go
generated
vendored
Normal file
53
server/vendor/github.com/pion/sctp/chunk_cookie_ack.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkCookieAck represents an SCTP Chunk of type chunkCookieAck
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 11 |Chunk Flags | Length = 4 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkCookieAck struct {
|
||||
chunkHeader
|
||||
}
|
||||
|
||||
// Cookie ack chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotCookieAck = errors.New("ChunkType is not of type COOKIEACK")
|
||||
)
|
||||
|
||||
func (c *chunkCookieAck) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.typ != ctCookieAck {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotCookieAck, c.typ.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkCookieAck) marshal() ([]byte, error) {
|
||||
c.chunkHeader.typ = ctCookieAck
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkCookieAck) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkCookieAck printable
|
||||
func (c *chunkCookieAck) String() string {
|
||||
return c.chunkHeader.String()
|
||||
}
|
||||
54
server/vendor/github.com/pion/sctp/chunk_cookie_echo.go
generated
vendored
Normal file
54
server/vendor/github.com/pion/sctp/chunk_cookie_echo.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
CookieEcho represents an SCTP Chunk of type CookieEcho
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 10 |Chunk Flags | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cookie |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkCookieEcho struct {
|
||||
chunkHeader
|
||||
cookie []byte
|
||||
}
|
||||
|
||||
// Cookie echo chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotCookieEcho = errors.New("ChunkType is not of type COOKIEECHO")
|
||||
)
|
||||
|
||||
func (c *chunkCookieEcho) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.typ != ctCookieEcho {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotCookieEcho, c.typ.String())
|
||||
}
|
||||
c.cookie = c.raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkCookieEcho) marshal() ([]byte, error) {
|
||||
c.chunkHeader.typ = ctCookieEcho
|
||||
c.chunkHeader.raw = c.cookie
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkCookieEcho) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
103
server/vendor/github.com/pion/sctp/chunk_error.go
generated
vendored
Normal file
103
server/vendor/github.com/pion/sctp/chunk_error.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp // nolint:dupl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Operation Error (ERROR) (9)
|
||||
|
||||
An endpoint sends this chunk to its peer endpoint to notify it of
|
||||
certain error conditions. It contains one or more error causes. An
|
||||
Operation Error is not considered fatal in and of itself, but may be
|
||||
used with an ERROR chunk to report a fatal condition. It has the
|
||||
following parameters:
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 9 | Chunk Flags | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
\ \
|
||||
/ one or more Error Causes /
|
||||
\ \
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Chunk Flags: 8 bits
|
||||
|
||||
Set to 0 on transmit and ignored on receipt.
|
||||
|
||||
Length: 16 bits (unsigned integer)
|
||||
|
||||
Set to the size of the chunk in bytes, including the chunk header
|
||||
and all the Error Cause fields present.
|
||||
*/
|
||||
type chunkError struct {
|
||||
chunkHeader
|
||||
errorCauses []errorCause
|
||||
}
|
||||
|
||||
// Error chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotCtError = errors.New("ChunkType is not of type ctError")
|
||||
ErrBuildErrorChunkFailed = errors.New("failed build Error Chunk")
|
||||
)
|
||||
|
||||
func (a *chunkError) unmarshal(raw []byte) error {
|
||||
if err := a.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.typ != ctError {
|
||||
return fmt.Errorf("%w, actually is %s", ErrChunkTypeNotCtError, a.typ.String())
|
||||
}
|
||||
|
||||
offset := chunkHeaderSize
|
||||
for {
|
||||
if len(raw)-offset < 4 {
|
||||
break
|
||||
}
|
||||
|
||||
e, err := buildErrorCause(raw[offset:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrBuildErrorChunkFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
offset += int(e.length())
|
||||
a.errorCauses = append(a.errorCauses, e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *chunkError) marshal() ([]byte, error) {
|
||||
a.chunkHeader.typ = ctError
|
||||
a.flags = 0x00
|
||||
a.raw = []byte{}
|
||||
for _, ec := range a.errorCauses {
|
||||
raw, err := ec.marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.raw = append(a.raw, raw...)
|
||||
}
|
||||
return a.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (a *chunkError) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkError printable
|
||||
func (a *chunkError) String() string {
|
||||
res := a.chunkHeader.String()
|
||||
|
||||
for _, cause := range a.errorCauses {
|
||||
res += fmt.Sprintf("\n - %s", cause)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
151
server/vendor/github.com/pion/sctp/chunk_forward_tsn.go
generated
vendored
Normal file
151
server/vendor/github.com/pion/sctp/chunk_forward_tsn.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// This chunk shall be used by the data sender to inform the data
|
||||
// receiver to adjust its cumulative received TSN point forward because
|
||||
// some missing TSNs are associated with data chunks that SHOULD NOT be
|
||||
// transmitted or retransmitted by the sender.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 192 | Flags = 0x00 | Length = Variable |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | New Cumulative TSN |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Stream-1 | Stream Sequence-1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// \ /
|
||||
// / \
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Stream-N | Stream Sequence-N |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type chunkForwardTSN struct {
|
||||
chunkHeader
|
||||
|
||||
// This indicates the new cumulative TSN to the data receiver. Upon
|
||||
// the reception of this value, the data receiver MUST consider
|
||||
// any missing TSNs earlier than or equal to this value as received,
|
||||
// and stop reporting them as gaps in any subsequent SACKs.
|
||||
newCumulativeTSN uint32
|
||||
|
||||
streams []chunkForwardTSNStream
|
||||
}
|
||||
|
||||
const (
|
||||
newCumulativeTSNLength = 4
|
||||
forwardTSNStreamLength = 4
|
||||
)
|
||||
|
||||
// Forward TSN chunk errors
|
||||
var (
|
||||
ErrMarshalStreamFailed = errors.New("failed to marshal stream")
|
||||
ErrChunkTooShort = errors.New("chunk too short")
|
||||
)
|
||||
|
||||
func (c *chunkForwardTSN) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(c.raw) < newCumulativeTSNLength {
|
||||
return ErrChunkTooShort
|
||||
}
|
||||
|
||||
c.newCumulativeTSN = binary.BigEndian.Uint32(c.raw[0:])
|
||||
|
||||
offset := newCumulativeTSNLength
|
||||
remaining := len(c.raw) - offset
|
||||
for remaining > 0 {
|
||||
s := chunkForwardTSNStream{}
|
||||
|
||||
if err := s.unmarshal(c.raw[offset:]); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
c.streams = append(c.streams, s)
|
||||
|
||||
offset += s.length()
|
||||
remaining -= s.length()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkForwardTSN) marshal() ([]byte, error) {
|
||||
out := make([]byte, newCumulativeTSNLength)
|
||||
binary.BigEndian.PutUint32(out[0:], c.newCumulativeTSN)
|
||||
|
||||
for _, s := range c.streams {
|
||||
b, err := s.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint
|
||||
}
|
||||
out = append(out, b...)
|
||||
}
|
||||
|
||||
c.typ = ctForwardTSN
|
||||
c.raw = out
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkForwardTSN) check() (abort bool, err error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// String makes chunkForwardTSN printable
|
||||
func (c *chunkForwardTSN) String() string {
|
||||
res := fmt.Sprintf("New Cumulative TSN: %d\n", c.newCumulativeTSN)
|
||||
for _, s := range c.streams {
|
||||
res += fmt.Sprintf(" - si=%d, ssn=%d\n", s.identifier, s.sequence)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type chunkForwardTSNStream struct {
|
||||
// This field holds a stream number that was skipped by this
|
||||
// FWD-TSN.
|
||||
identifier uint16
|
||||
|
||||
// This field holds the sequence number associated with the stream
|
||||
// that was skipped. The stream sequence field holds the largest
|
||||
// stream sequence number in this stream being skipped. The receiver
|
||||
// of the FWD-TSN's can use the Stream-N and Stream Sequence-N fields
|
||||
// to enable delivery of any stranded TSN's that remain on the stream
|
||||
// re-ordering queues. This field MUST NOT report TSN's corresponding
|
||||
// to DATA chunks that are marked as unordered. For ordered DATA
|
||||
// chunks this field MUST be filled in.
|
||||
sequence uint16
|
||||
}
|
||||
|
||||
func (s *chunkForwardTSNStream) length() int {
|
||||
return forwardTSNStreamLength
|
||||
}
|
||||
|
||||
func (s *chunkForwardTSNStream) unmarshal(raw []byte) error {
|
||||
if len(raw) < forwardTSNStreamLength {
|
||||
return ErrChunkTooShort
|
||||
}
|
||||
s.identifier = binary.BigEndian.Uint16(raw[0:])
|
||||
s.sequence = binary.BigEndian.Uint16(raw[2:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *chunkForwardTSNStream) marshal() ([]byte, error) { // nolint:unparam
|
||||
out := make([]byte, forwardTSNStreamLength)
|
||||
|
||||
binary.BigEndian.PutUint16(out[0:], s.identifier)
|
||||
binary.BigEndian.PutUint16(out[2:], s.sequence)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
86
server/vendor/github.com/pion/sctp/chunk_heartbeat.go
generated
vendored
Normal file
86
server/vendor/github.com/pion/sctp/chunk_heartbeat.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkHeartbeat represents an SCTP Chunk of type HEARTBEAT
|
||||
|
||||
An endpoint should send this chunk to its peer endpoint to probe the
|
||||
reachability of a particular destination transport address defined in
|
||||
the present association.
|
||||
|
||||
The parameter field contains the Heartbeat Information, which is a
|
||||
variable-length opaque data structure understood only by the sender.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 4 | Chunk Flags | Heartbeat Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Heartbeat Information TLV (Variable-Length) |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Defined as a variable-length parameter using the format described
|
||||
in Section 3.2.1, i.e.:
|
||||
|
||||
Variable Parameters Status Type Value
|
||||
-------------------------------------------------------------
|
||||
heartbeat Info Mandatory 1
|
||||
*/
|
||||
type chunkHeartbeat struct {
|
||||
chunkHeader
|
||||
params []param
|
||||
}
|
||||
|
||||
// Heartbeat chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotHeartbeat = errors.New("ChunkType is not of type HEARTBEAT")
|
||||
ErrHeartbeatNotLongEnoughInfo = errors.New("heartbeat is not long enough to contain Heartbeat Info")
|
||||
ErrParseParamTypeFailed = errors.New("failed to parse param type")
|
||||
ErrHeartbeatParam = errors.New("heartbeat should only have HEARTBEAT param")
|
||||
ErrHeartbeatChunkUnmarshal = errors.New("failed unmarshalling param in Heartbeat Chunk")
|
||||
)
|
||||
|
||||
func (h *chunkHeartbeat) unmarshal(raw []byte) error {
|
||||
if err := h.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
} else if h.typ != ctHeartbeat {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotHeartbeat, h.typ.String())
|
||||
}
|
||||
|
||||
if len(raw) <= chunkHeaderSize {
|
||||
return fmt.Errorf("%w: %d", ErrHeartbeatNotLongEnoughInfo, len(raw))
|
||||
}
|
||||
|
||||
pType, err := parseParamType(raw[chunkHeaderSize:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrParseParamTypeFailed, err) //nolint:errorlint
|
||||
}
|
||||
if pType != heartbeatInfo {
|
||||
return fmt.Errorf("%w: instead have %s", ErrHeartbeatParam, pType.String())
|
||||
}
|
||||
|
||||
p, err := buildParam(pType, raw[chunkHeaderSize:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrHeartbeatChunkUnmarshal, err) //nolint:errorlint
|
||||
}
|
||||
h.params = append(h.params, p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *chunkHeartbeat) Marshal() ([]byte, error) {
|
||||
return nil, ErrUnimplemented
|
||||
}
|
||||
|
||||
func (h *chunkHeartbeat) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
96
server/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go
generated
vendored
Normal file
96
server/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkHeartbeatAck represents an SCTP Chunk of type HEARTBEAT ACK
|
||||
|
||||
An endpoint should send this chunk to its peer endpoint as a response
|
||||
to a HEARTBEAT chunk (see Section 8.3). A HEARTBEAT ACK is always
|
||||
sent to the source IP address of the IP datagram containing the
|
||||
HEARTBEAT chunk to which this ack is responding.
|
||||
|
||||
The parameter field contains a variable-length opaque data structure.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 5 | Chunk Flags | Heartbeat Ack Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Heartbeat Information TLV (Variable-Length) |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Defined as a variable-length parameter using the format described
|
||||
in Section 3.2.1, i.e.:
|
||||
|
||||
Variable Parameters Status Type Value
|
||||
-------------------------------------------------------------
|
||||
Heartbeat Info Mandatory 1
|
||||
*/
|
||||
type chunkHeartbeatAck struct {
|
||||
chunkHeader
|
||||
params []param
|
||||
}
|
||||
|
||||
// Heartbeat ack chunk errors
|
||||
var (
|
||||
ErrUnimplemented = errors.New("unimplemented")
|
||||
ErrHeartbeatAckParams = errors.New("heartbeat Ack must have one param")
|
||||
ErrHeartbeatAckNotHeartbeatInfo = errors.New("heartbeat Ack must have one param, and it should be a HeartbeatInfo")
|
||||
ErrHeartbeatAckMarshalParam = errors.New("unable to marshal parameter for Heartbeat Ack")
|
||||
)
|
||||
|
||||
func (h *chunkHeartbeatAck) unmarshal([]byte) error {
|
||||
return ErrUnimplemented
|
||||
}
|
||||
|
||||
func (h *chunkHeartbeatAck) marshal() ([]byte, error) {
|
||||
if len(h.params) != 1 {
|
||||
return nil, ErrHeartbeatAckParams
|
||||
}
|
||||
|
||||
switch h.params[0].(type) {
|
||||
case *paramHeartbeatInfo:
|
||||
// ParamHeartbeatInfo is valid
|
||||
default:
|
||||
return nil, ErrHeartbeatAckNotHeartbeatInfo
|
||||
}
|
||||
|
||||
out := make([]byte, 0)
|
||||
for idx, p := range h.params {
|
||||
pp, err := p.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrHeartbeatAckMarshalParam, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
out = append(out, pp...)
|
||||
|
||||
// Chunks (including Type, Length, and Value fields) are padded out
|
||||
// by the sender with all zero bytes to be a multiple of 4 bytes
|
||||
// long. This padding MUST NOT be more than 3 bytes in total. The
|
||||
// Chunk Length value does not include terminating padding of the
|
||||
// chunk. *However, it does include padding of any variable-length
|
||||
// parameter except the last parameter in the chunk.* The receiver
|
||||
// MUST ignore the padding.
|
||||
if idx != len(h.params)-1 {
|
||||
out = padByte(out, getPadding(len(pp)))
|
||||
}
|
||||
}
|
||||
|
||||
h.chunkHeader.typ = ctHeartbeatAck
|
||||
h.chunkHeader.raw = out
|
||||
|
||||
return h.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (h *chunkHeartbeatAck) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
142
server/vendor/github.com/pion/sctp/chunk_init.go
generated
vendored
Normal file
142
server/vendor/github.com/pion/sctp/chunk_init.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
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")
|
||||
ErrInitUnknownParam = errors.New("INIT with unknown param")
|
||||
)
|
||||
|
||||
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) //nolint:errorlint
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *chunkInit) marshal() ([]byte, error) {
|
||||
initShared, err := i.chunkInitCommon.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrChunkTypeInitMarshalFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
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 {
|
||||
return true, 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 {
|
||||
return true, 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 {
|
||||
return true, 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 {
|
||||
return true, ErrInitAdvertisedReceiver1500
|
||||
}
|
||||
|
||||
for _, p := range i.unrecognizedParams {
|
||||
if p.unrecognizedAction == paramHeaderUnrecognizedActionStop ||
|
||||
p.unrecognizedAction == paramHeaderUnrecognizedActionStopAndReport {
|
||||
return true, ErrInitUnknownParam
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkInit printable
|
||||
func (i *chunkInit) String() string {
|
||||
return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
|
||||
}
|
||||
140
server/vendor/github.com/pion/sctp/chunk_init_ack.go
generated
vendored
Normal file
140
server/vendor/github.com/pion/sctp/chunk_init_ack.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp // nolint:dupl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkInitAck represents an SCTP Chunk of type INIT ACK
|
||||
|
||||
See chunkInitCommon for the fixed headers
|
||||
|
||||
Variable Parameters Status Type Value
|
||||
-------------------------------------------------------------
|
||||
State Cookie Mandatory 7
|
||||
IPv4 IP (Note 1) Optional 5
|
||||
IPv6 IP (Note 1) Optional 6
|
||||
Unrecognized Parameter Optional 8
|
||||
Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
|
||||
Host Name IP (Note 3) Optional 11<Paste>
|
||||
*/
|
||||
type chunkInitAck struct {
|
||||
chunkHeader
|
||||
chunkInitCommon
|
||||
}
|
||||
|
||||
// Init ack chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotInitAck = errors.New("ChunkType is not of type INIT ACK")
|
||||
ErrChunkNotLongEnoughForParams = errors.New("chunk Value isn't long enough for mandatory parameters exp")
|
||||
ErrChunkTypeInitAckFlagZero = errors.New("ChunkType of type INIT ACK flags must be all 0")
|
||||
ErrInitAckUnmarshalFailed = errors.New("failed to unmarshal INIT body")
|
||||
ErrInitCommonDataMarshalFailed = errors.New("failed marshaling INIT common data")
|
||||
ErrChunkTypeInitAckInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0")
|
||||
ErrInitAckInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0")
|
||||
ErrInitAckOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0")
|
||||
ErrInitAckAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500")
|
||||
)
|
||||
|
||||
func (i *chunkInitAck) unmarshal(raw []byte) error {
|
||||
if err := i.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.typ != ctInitAck {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotInitAck, i.typ.String())
|
||||
} else if len(i.raw) < initChunkMinLength {
|
||||
return fmt.Errorf("%w: %d actual: %d", ErrChunkNotLongEnoughForParams, 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 ErrChunkTypeInitAckFlagZero
|
||||
}
|
||||
|
||||
if err := i.chunkInitCommon.unmarshal(i.raw); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrInitAckUnmarshalFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *chunkInitAck) marshal() ([]byte, error) {
|
||||
initShared, err := i.chunkInitCommon.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrInitCommonDataMarshalFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
i.chunkHeader.typ = ctInitAck
|
||||
i.chunkHeader.raw = initShared
|
||||
return i.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (i *chunkInitAck) check() (abort bool, err error) {
|
||||
// The receiver of the INIT ACK records the value of the Initiate Tag
|
||||
// parameter. This value MUST be placed into the Verification Tag
|
||||
// field of every SCTP packet that the INIT ACK receiver transmits
|
||||
// within this association.
|
||||
//
|
||||
// The Initiate Tag MUST NOT take the value 0. See Section 5.3.1 for
|
||||
// more on the selection of the Initiate Tag value.
|
||||
//
|
||||
// If the value of the Initiate Tag in a received INIT ACK chunk is
|
||||
// found to be 0, the receiver MUST destroy the association
|
||||
// discarding its TCB. The receiver MAY send an ABORT for debugging
|
||||
// purpose.
|
||||
if i.initiateTag == 0 {
|
||||
abort = true
|
||||
return abort, ErrChunkTypeInitAckInitateTagZero
|
||||
}
|
||||
|
||||
// Defines the maximum number of streams the sender of this INIT ACK
|
||||
// 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 ACK with the MIS value set to 0 SHOULD
|
||||
// destroy the association discarding its TCB.
|
||||
if i.numInboundStreams == 0 {
|
||||
abort = true
|
||||
return abort, ErrInitAckInboundStreamRequestZero
|
||||
}
|
||||
|
||||
// Defines the number of outbound streams the sender of this INIT ACK
|
||||
// chunk wishes to create in this association. The value of 0 MUST
|
||||
// NOT be used, and the value MUST NOT be greater than the MIS value
|
||||
// sent in the INIT chunk.
|
||||
//
|
||||
// Note: A receiver of an INIT ACK with the OS value set to 0 SHOULD
|
||||
// destroy the association discarding its TCB.
|
||||
|
||||
if i.numOutboundStreams == 0 {
|
||||
abort = true
|
||||
return abort, ErrInitAckOutboundStreamRequestZero
|
||||
}
|
||||
|
||||
// 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, ErrInitAckAdvertisedReceiver1500
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkInitAck printable
|
||||
func (i *chunkInitAck) String() string {
|
||||
return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
|
||||
}
|
||||
167
server/vendor/github.com/pion/sctp/chunk_init_common.go
generated
vendored
Normal file
167
server/vendor/github.com/pion/sctp/chunk_init_common.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 1 | Chunk Flags | Chunk Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Initiate Tag |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Advertised Receiver Window Credit (a_rwnd) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Outbound Streams | Number of Inbound Streams |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Initial TSN |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Optional/Variable-Length Parameters |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
The INIT chunk contains the following parameters. Unless otherwise
|
||||
noted, each parameter MUST only be included once in the INIT chunk.
|
||||
|
||||
Fixed Parameters Status
|
||||
----------------------------------------------
|
||||
Initiate Tag Mandatory
|
||||
Advertised Receiver Window Credit Mandatory
|
||||
Number of Outbound Streams Mandatory
|
||||
Number of Inbound Streams Mandatory
|
||||
Initial TSN Mandatory
|
||||
*/
|
||||
|
||||
type chunkInitCommon struct {
|
||||
initiateTag uint32
|
||||
advertisedReceiverWindowCredit uint32
|
||||
numOutboundStreams uint16
|
||||
numInboundStreams uint16
|
||||
initialTSN uint32
|
||||
params []param
|
||||
unrecognizedParams []paramHeader
|
||||
}
|
||||
|
||||
const (
|
||||
initChunkMinLength = 16
|
||||
initOptionalVarHeaderLength = 4
|
||||
)
|
||||
|
||||
// Init chunk errors
|
||||
var (
|
||||
ErrInitChunkParseParamTypeFailed = errors.New("failed to parse param type")
|
||||
ErrInitAckMarshalParam = errors.New("unable to marshal parameter for INIT/INITACK")
|
||||
)
|
||||
|
||||
func (i *chunkInitCommon) unmarshal(raw []byte) error {
|
||||
i.initiateTag = binary.BigEndian.Uint32(raw[0:])
|
||||
i.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(raw[4:])
|
||||
i.numOutboundStreams = binary.BigEndian.Uint16(raw[8:])
|
||||
i.numInboundStreams = binary.BigEndian.Uint16(raw[10:])
|
||||
i.initialTSN = binary.BigEndian.Uint32(raw[12:])
|
||||
|
||||
// https://tools.ietf.org/html/rfc4960#section-3.2.1
|
||||
//
|
||||
// Chunk values of SCTP control chunks consist of a chunk-type-specific
|
||||
// header of required fields, followed by zero or more parameters. The
|
||||
// optional and variable-length parameters contained in a chunk are
|
||||
// defined in a Type-Length-Value format as shown below.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Parameter Type | Parameter Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// | Parameter Value |
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
offset := initChunkMinLength
|
||||
remaining := len(raw) - offset
|
||||
for remaining > 0 {
|
||||
if remaining > initOptionalVarHeaderLength {
|
||||
var pHeader paramHeader
|
||||
if err := pHeader.unmarshal(raw[offset:]); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrInitChunkParseParamTypeFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
p, err := buildParam(pHeader.typ, raw[offset:])
|
||||
if err != nil {
|
||||
i.unrecognizedParams = append(i.unrecognizedParams, pHeader)
|
||||
} else {
|
||||
i.params = append(i.params, p)
|
||||
}
|
||||
|
||||
padding := getPadding(pHeader.length())
|
||||
offset += pHeader.length() + padding
|
||||
remaining -= pHeader.length() + padding
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *chunkInitCommon) marshal() ([]byte, error) {
|
||||
out := make([]byte, initChunkMinLength)
|
||||
binary.BigEndian.PutUint32(out[0:], i.initiateTag)
|
||||
binary.BigEndian.PutUint32(out[4:], i.advertisedReceiverWindowCredit)
|
||||
binary.BigEndian.PutUint16(out[8:], i.numOutboundStreams)
|
||||
binary.BigEndian.PutUint16(out[10:], i.numInboundStreams)
|
||||
binary.BigEndian.PutUint32(out[12:], i.initialTSN)
|
||||
for idx, p := range i.params {
|
||||
pp, err := p.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrInitAckMarshalParam, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
out = append(out, pp...)
|
||||
|
||||
// Chunks (including Type, Length, and Value fields) are padded out
|
||||
// by the sender with all zero bytes to be a multiple of 4 bytes
|
||||
// long. This padding MUST NOT be more than 3 bytes in total. The
|
||||
// Chunk Length value does not include terminating padding of the
|
||||
// chunk. *However, it does include padding of any variable-length
|
||||
// parameter except the last parameter in the chunk.* The receiver
|
||||
// MUST ignore the padding.
|
||||
if idx != len(i.params)-1 {
|
||||
out = padByte(out, getPadding(len(pp)))
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// String makes chunkInitCommon printable
|
||||
func (i chunkInitCommon) String() string {
|
||||
format := `initiateTag: %d
|
||||
advertisedReceiverWindowCredit: %d
|
||||
numOutboundStreams: %d
|
||||
numInboundStreams: %d
|
||||
initialTSN: %d`
|
||||
|
||||
res := fmt.Sprintf(format,
|
||||
i.initiateTag,
|
||||
i.advertisedReceiverWindowCredit,
|
||||
i.numOutboundStreams,
|
||||
i.numInboundStreams,
|
||||
i.initialTSN,
|
||||
)
|
||||
|
||||
for i, param := range i.params {
|
||||
res += fmt.Sprintf("Param %d:\n %s", i, param)
|
||||
}
|
||||
return res
|
||||
}
|
||||
212
server/vendor/github.com/pion/sctp/chunk_payload_data.go
generated
vendored
Normal file
212
server/vendor/github.com/pion/sctp/chunk_payload_data.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkPayloadData represents an SCTP Chunk of type DATA
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 0 | Reserved|U|B|E| Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| TSN |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Stream Identifier S | Stream Sequence Number n |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Payload Protocol Identifier |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| User Data (seq n of Stream S) |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
An unfragmented user message shall have both the B and E bits set to
|
||||
'1'. Setting both B and E bits to '0' indicates a middle fragment of
|
||||
a multi-fragment user message, as summarized in the following table:
|
||||
|
||||
B E Description
|
||||
============================================================
|
||||
| 1 0 | First piece of a fragmented user message |
|
||||
+----------------------------------------------------------+
|
||||
| 0 0 | Middle piece of a fragmented user message |
|
||||
+----------------------------------------------------------+
|
||||
| 0 1 | Last piece of a fragmented user message |
|
||||
+----------------------------------------------------------+
|
||||
| 1 1 | Unfragmented message |
|
||||
============================================================
|
||||
| Table 1: Fragment Description Flags |
|
||||
============================================================
|
||||
*/
|
||||
type chunkPayloadData struct {
|
||||
chunkHeader
|
||||
|
||||
unordered bool
|
||||
beginningFragment bool
|
||||
endingFragment bool
|
||||
immediateSack bool
|
||||
|
||||
tsn uint32
|
||||
streamIdentifier uint16
|
||||
streamSequenceNumber uint16
|
||||
payloadType PayloadProtocolIdentifier
|
||||
userData []byte
|
||||
|
||||
// Whether this data chunk was acknowledged (received by peer)
|
||||
acked bool
|
||||
missIndicator uint32
|
||||
|
||||
// Partial-reliability parameters used only by sender
|
||||
since time.Time
|
||||
nSent uint32 // number of transmission made for this chunk
|
||||
_abandoned bool
|
||||
_allInflight bool // valid only with the first fragment
|
||||
|
||||
// Retransmission flag set when T1-RTX timeout occurred and this
|
||||
// chunk is still in the inflight queue
|
||||
retransmit bool
|
||||
|
||||
head *chunkPayloadData // link to the head of the fragment
|
||||
}
|
||||
|
||||
const (
|
||||
payloadDataEndingFragmentBitmask = 1
|
||||
payloadDataBeginingFragmentBitmask = 2
|
||||
payloadDataUnorderedBitmask = 4
|
||||
payloadDataImmediateSACK = 8
|
||||
|
||||
payloadDataHeaderSize = 12
|
||||
)
|
||||
|
||||
// PayloadProtocolIdentifier is an enum for DataChannel payload types
|
||||
type PayloadProtocolIdentifier uint32
|
||||
|
||||
// PayloadProtocolIdentifier enums
|
||||
// https://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml#sctp-parameters-25
|
||||
const (
|
||||
PayloadTypeUnknown PayloadProtocolIdentifier = 0
|
||||
PayloadTypeWebRTCDCEP PayloadProtocolIdentifier = 50
|
||||
PayloadTypeWebRTCString PayloadProtocolIdentifier = 51
|
||||
PayloadTypeWebRTCBinary PayloadProtocolIdentifier = 53
|
||||
PayloadTypeWebRTCStringEmpty PayloadProtocolIdentifier = 56
|
||||
PayloadTypeWebRTCBinaryEmpty PayloadProtocolIdentifier = 57
|
||||
)
|
||||
|
||||
// Data chunk errors
|
||||
var (
|
||||
ErrChunkPayloadSmall = errors.New("packet is smaller than the header size")
|
||||
)
|
||||
|
||||
func (p PayloadProtocolIdentifier) String() string {
|
||||
switch p {
|
||||
case PayloadTypeWebRTCDCEP:
|
||||
return "WebRTC DCEP"
|
||||
case PayloadTypeWebRTCString:
|
||||
return "WebRTC String"
|
||||
case PayloadTypeWebRTCBinary:
|
||||
return "WebRTC Binary"
|
||||
case PayloadTypeWebRTCStringEmpty:
|
||||
return "WebRTC String (Empty)"
|
||||
case PayloadTypeWebRTCBinaryEmpty:
|
||||
return "WebRTC Binary (Empty)"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown Payload Protocol Identifier: %d", p)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) unmarshal(raw []byte) error {
|
||||
if err := p.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.immediateSack = p.flags&payloadDataImmediateSACK != 0
|
||||
p.unordered = p.flags&payloadDataUnorderedBitmask != 0
|
||||
p.beginningFragment = p.flags&payloadDataBeginingFragmentBitmask != 0
|
||||
p.endingFragment = p.flags&payloadDataEndingFragmentBitmask != 0
|
||||
|
||||
if len(p.raw) < payloadDataHeaderSize {
|
||||
return ErrChunkPayloadSmall
|
||||
}
|
||||
p.tsn = binary.BigEndian.Uint32(p.raw[0:])
|
||||
p.streamIdentifier = binary.BigEndian.Uint16(p.raw[4:])
|
||||
p.streamSequenceNumber = binary.BigEndian.Uint16(p.raw[6:])
|
||||
p.payloadType = PayloadProtocolIdentifier(binary.BigEndian.Uint32(p.raw[8:]))
|
||||
p.userData = p.raw[payloadDataHeaderSize:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) marshal() ([]byte, error) {
|
||||
payRaw := make([]byte, payloadDataHeaderSize+len(p.userData))
|
||||
|
||||
binary.BigEndian.PutUint32(payRaw[0:], p.tsn)
|
||||
binary.BigEndian.PutUint16(payRaw[4:], p.streamIdentifier)
|
||||
binary.BigEndian.PutUint16(payRaw[6:], p.streamSequenceNumber)
|
||||
binary.BigEndian.PutUint32(payRaw[8:], uint32(p.payloadType))
|
||||
copy(payRaw[payloadDataHeaderSize:], p.userData)
|
||||
|
||||
flags := uint8(0)
|
||||
if p.endingFragment {
|
||||
flags = 1
|
||||
}
|
||||
if p.beginningFragment {
|
||||
flags |= 1 << 1
|
||||
}
|
||||
if p.unordered {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
if p.immediateSack {
|
||||
flags |= 1 << 3
|
||||
}
|
||||
|
||||
p.chunkHeader.flags = flags
|
||||
p.chunkHeader.typ = ctPayloadData
|
||||
p.chunkHeader.raw = payRaw
|
||||
return p.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkPayloadData printable
|
||||
func (p *chunkPayloadData) String() string {
|
||||
return fmt.Sprintf("%s\n%d", p.chunkHeader, p.tsn)
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) abandoned() bool {
|
||||
if p.head != nil {
|
||||
return p.head._abandoned && p.head._allInflight
|
||||
}
|
||||
return p._abandoned && p._allInflight
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) setAbandoned(abandoned bool) {
|
||||
if p.head != nil {
|
||||
p.head._abandoned = abandoned
|
||||
return
|
||||
}
|
||||
p._abandoned = abandoned
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) setAllInflight() {
|
||||
if p.endingFragment {
|
||||
if p.head != nil {
|
||||
p.head._allInflight = true
|
||||
} else {
|
||||
p._allInflight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *chunkPayloadData) isFragmented() bool {
|
||||
return !(p.head == nil && p.beginningFragment && p.endingFragment)
|
||||
}
|
||||
108
server/vendor/github.com/pion/sctp/chunk_reconfig.go
generated
vendored
Normal file
108
server/vendor/github.com/pion/sctp/chunk_reconfig.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// https://tools.ietf.org/html/rfc6525#section-3.1
|
||||
// chunkReconfig represents an SCTP Chunk used to reconfigure streams.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 130 | Chunk Flags | Chunk Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// \ \
|
||||
// / Re-configuration Parameter /
|
||||
// \ \
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// \ \
|
||||
// / Re-configuration Parameter (optional) /
|
||||
// \ \
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type chunkReconfig struct {
|
||||
chunkHeader
|
||||
paramA param
|
||||
paramB param
|
||||
}
|
||||
|
||||
// Reconfigure chunk errors
|
||||
var (
|
||||
ErrChunkParseParamTypeFailed = errors.New("failed to parse param type")
|
||||
ErrChunkMarshalParamAReconfigFailed = errors.New("unable to marshal parameter A for reconfig")
|
||||
ErrChunkMarshalParamBReconfigFailed = errors.New("unable to marshal parameter B for reconfig")
|
||||
)
|
||||
|
||||
func (c *chunkReconfig) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
pType, err := parseParamType(c.raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrChunkParseParamTypeFailed, err) //nolint:errorlint
|
||||
}
|
||||
a, err := buildParam(pType, c.raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.paramA = a
|
||||
|
||||
padding := getPadding(a.length())
|
||||
offset := a.length() + padding
|
||||
if len(c.raw) > offset {
|
||||
pType, err := parseParamType(c.raw[offset:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrChunkParseParamTypeFailed, err) //nolint:errorlint
|
||||
}
|
||||
b, err := buildParam(pType, c.raw[offset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.paramB = b
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkReconfig) marshal() ([]byte, error) {
|
||||
out, err := c.paramA.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrChunkMarshalParamAReconfigFailed, err) //nolint:errorlint
|
||||
}
|
||||
if c.paramB != nil {
|
||||
// Pad param A
|
||||
out = padByte(out, getPadding(len(out)))
|
||||
|
||||
outB, err := c.paramB.marshal()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrChunkMarshalParamBReconfigFailed, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
out = append(out, outB...)
|
||||
}
|
||||
|
||||
c.typ = ctReconfig
|
||||
c.raw = out
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkReconfig) check() (abort bool, err error) {
|
||||
// nolint:godox
|
||||
// TODO: check allowed combinations:
|
||||
// https://tools.ietf.org/html/rfc6525#section-3.1
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// String makes chunkReconfig printable
|
||||
func (c *chunkReconfig) String() string {
|
||||
res := fmt.Sprintf("Param A:\n %s", c.paramA)
|
||||
if c.paramB != nil {
|
||||
res += fmt.Sprintf("Param B:\n %s", c.paramB)
|
||||
}
|
||||
return res
|
||||
}
|
||||
151
server/vendor/github.com/pion/sctp/chunk_selective_ack.go
generated
vendored
Normal file
151
server/vendor/github.com/pion/sctp/chunk_selective_ack.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkSelectiveAck represents an SCTP Chunk of type SACK
|
||||
|
||||
This chunk is sent to the peer endpoint to acknowledge received DATA
|
||||
chunks and to inform the peer endpoint of gaps in the received
|
||||
subsequences of DATA chunks as represented by their TSNs.
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 3 |Chunk Flags | Chunk Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cumulative TSN Ack |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Advertised Receiver Window Credit (a_rwnd) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Gap Ack Blocks = N | Number of Duplicate TSNs = X |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Gap Ack Block #1 Start | Gap Ack Block #1 End |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ ... \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Gap Ack Block #N Start | Gap Ack Block #N End |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Duplicate TSN 1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ ... \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Duplicate TSN X |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
type gapAckBlock struct {
|
||||
start uint16
|
||||
end uint16
|
||||
}
|
||||
|
||||
// Selective ack chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotSack = errors.New("ChunkType is not of type SACK")
|
||||
ErrSackSizeNotLargeEnoughInfo = errors.New("SACK Chunk size is not large enough to contain header")
|
||||
ErrSackSizeNotMatchPredicted = errors.New("SACK Chunk size does not match predicted amount from header values")
|
||||
)
|
||||
|
||||
// String makes gapAckBlock printable
|
||||
func (g gapAckBlock) String() string {
|
||||
return fmt.Sprintf("%d - %d", g.start, g.end)
|
||||
}
|
||||
|
||||
type chunkSelectiveAck struct {
|
||||
chunkHeader
|
||||
cumulativeTSNAck uint32
|
||||
advertisedReceiverWindowCredit uint32
|
||||
gapAckBlocks []gapAckBlock
|
||||
duplicateTSN []uint32
|
||||
}
|
||||
|
||||
const (
|
||||
selectiveAckHeaderSize = 12
|
||||
)
|
||||
|
||||
func (s *chunkSelectiveAck) unmarshal(raw []byte) error {
|
||||
if err := s.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.typ != ctSack {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotSack, s.typ.String())
|
||||
}
|
||||
|
||||
if len(s.raw) < selectiveAckHeaderSize {
|
||||
return fmt.Errorf("%w: %v remaining, needs %v bytes", ErrSackSizeNotLargeEnoughInfo,
|
||||
len(s.raw), selectiveAckHeaderSize)
|
||||
}
|
||||
|
||||
s.cumulativeTSNAck = binary.BigEndian.Uint32(s.raw[0:])
|
||||
s.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(s.raw[4:])
|
||||
s.gapAckBlocks = make([]gapAckBlock, binary.BigEndian.Uint16(s.raw[8:]))
|
||||
s.duplicateTSN = make([]uint32, binary.BigEndian.Uint16(s.raw[10:]))
|
||||
|
||||
if len(s.raw) != selectiveAckHeaderSize+(4*len(s.gapAckBlocks)+(4*len(s.duplicateTSN))) {
|
||||
return ErrSackSizeNotMatchPredicted
|
||||
}
|
||||
|
||||
offset := selectiveAckHeaderSize
|
||||
for i := range s.gapAckBlocks {
|
||||
s.gapAckBlocks[i].start = binary.BigEndian.Uint16(s.raw[offset:])
|
||||
s.gapAckBlocks[i].end = binary.BigEndian.Uint16(s.raw[offset+2:])
|
||||
offset += 4
|
||||
}
|
||||
for i := range s.duplicateTSN {
|
||||
s.duplicateTSN[i] = binary.BigEndian.Uint32(s.raw[offset:])
|
||||
offset += 4
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *chunkSelectiveAck) marshal() ([]byte, error) {
|
||||
sackRaw := make([]byte, selectiveAckHeaderSize+(4*len(s.gapAckBlocks)+(4*len(s.duplicateTSN))))
|
||||
binary.BigEndian.PutUint32(sackRaw[0:], s.cumulativeTSNAck)
|
||||
binary.BigEndian.PutUint32(sackRaw[4:], s.advertisedReceiverWindowCredit)
|
||||
binary.BigEndian.PutUint16(sackRaw[8:], uint16(len(s.gapAckBlocks)))
|
||||
binary.BigEndian.PutUint16(sackRaw[10:], uint16(len(s.duplicateTSN)))
|
||||
offset := selectiveAckHeaderSize
|
||||
for _, g := range s.gapAckBlocks {
|
||||
binary.BigEndian.PutUint16(sackRaw[offset:], g.start)
|
||||
binary.BigEndian.PutUint16(sackRaw[offset+2:], g.end)
|
||||
offset += 4
|
||||
}
|
||||
for _, t := range s.duplicateTSN {
|
||||
binary.BigEndian.PutUint32(sackRaw[offset:], t)
|
||||
offset += 4
|
||||
}
|
||||
|
||||
s.chunkHeader.typ = ctSack
|
||||
s.chunkHeader.raw = sackRaw
|
||||
return s.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (s *chunkSelectiveAck) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkSelectiveAck printable
|
||||
func (s *chunkSelectiveAck) String() string {
|
||||
res := fmt.Sprintf("SACK cumTsnAck=%d arwnd=%d dupTsn=%d",
|
||||
s.cumulativeTSNAck,
|
||||
s.advertisedReceiverWindowCredit,
|
||||
s.duplicateTSN)
|
||||
|
||||
for _, gap := range s.gapAckBlocks {
|
||||
res = fmt.Sprintf("%s\n gap ack: %s", res, gap)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
72
server/vendor/github.com/pion/sctp/chunk_shutdown.go
generated
vendored
Normal file
72
server/vendor/github.com/pion/sctp/chunk_shutdown.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkShutdown represents an SCTP Chunk of type chunkShutdown
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 7 | Chunk Flags | Length = 8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cumulative TSN Ack |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkShutdown struct {
|
||||
chunkHeader
|
||||
cumulativeTSNAck uint32
|
||||
}
|
||||
|
||||
const (
|
||||
cumulativeTSNAckLength = 4
|
||||
)
|
||||
|
||||
// Shutdown chunk errors
|
||||
var (
|
||||
ErrInvalidChunkSize = errors.New("invalid chunk size")
|
||||
ErrChunkTypeNotShutdown = errors.New("ChunkType is not of type SHUTDOWN")
|
||||
)
|
||||
|
||||
func (c *chunkShutdown) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.typ != ctShutdown {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdown, c.typ.String())
|
||||
}
|
||||
|
||||
if len(c.raw) != cumulativeTSNAckLength {
|
||||
return ErrInvalidChunkSize
|
||||
}
|
||||
|
||||
c.cumulativeTSNAck = binary.BigEndian.Uint32(c.raw[0:])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkShutdown) marshal() ([]byte, error) {
|
||||
out := make([]byte, cumulativeTSNAckLength)
|
||||
binary.BigEndian.PutUint32(out[0:], c.cumulativeTSNAck)
|
||||
|
||||
c.typ = ctShutdown
|
||||
c.raw = out
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkShutdown) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkShutdown printable
|
||||
func (c *chunkShutdown) String() string {
|
||||
return c.chunkHeader.String()
|
||||
}
|
||||
53
server/vendor/github.com/pion/sctp/chunk_shutdown_ack.go
generated
vendored
Normal file
53
server/vendor/github.com/pion/sctp/chunk_shutdown_ack.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkShutdownAck represents an SCTP Chunk of type chunkShutdownAck
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 8 | Chunk Flags | Length = 4 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkShutdownAck struct {
|
||||
chunkHeader
|
||||
}
|
||||
|
||||
// Shutdown ack chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotShutdownAck = errors.New("ChunkType is not of type SHUTDOWN-ACK")
|
||||
)
|
||||
|
||||
func (c *chunkShutdownAck) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.typ != ctShutdownAck {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdownAck, c.typ.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkShutdownAck) marshal() ([]byte, error) {
|
||||
c.typ = ctShutdownAck
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkShutdownAck) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkShutdownAck printable
|
||||
func (c *chunkShutdownAck) String() string {
|
||||
return c.chunkHeader.String()
|
||||
}
|
||||
53
server/vendor/github.com/pion/sctp/chunk_shutdown_complete.go
generated
vendored
Normal file
53
server/vendor/github.com/pion/sctp/chunk_shutdown_complete.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkShutdownComplete represents an SCTP Chunk of type chunkShutdownComplete
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 14 |Reserved |T| Length = 4 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkShutdownComplete struct {
|
||||
chunkHeader
|
||||
}
|
||||
|
||||
// Shutdown complete chunk errors
|
||||
var (
|
||||
ErrChunkTypeNotShutdownComplete = errors.New("ChunkType is not of type SHUTDOWN-COMPLETE")
|
||||
)
|
||||
|
||||
func (c *chunkShutdownComplete) unmarshal(raw []byte) error {
|
||||
if err := c.chunkHeader.unmarshal(raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.typ != ctShutdownComplete {
|
||||
return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdownComplete, c.typ.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkShutdownComplete) marshal() ([]byte, error) {
|
||||
c.typ = ctShutdownComplete
|
||||
return c.chunkHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *chunkShutdownComplete) check() (abort bool, err error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// String makes chunkShutdownComplete printable
|
||||
func (c *chunkShutdownComplete) String() string {
|
||||
return c.chunkHeader.String()
|
||||
}
|
||||
100
server/vendor/github.com/pion/sctp/chunkheader.go
generated
vendored
Normal file
100
server/vendor/github.com/pion/sctp/chunkheader.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
chunkHeader represents a SCTP Chunk header, defined in https://tools.ietf.org/html/rfc4960#section-3.2
|
||||
The figure below illustrates the field format for the chunks to be
|
||||
transmitted in the SCTP packet. Each chunk is formatted with a Chunk
|
||||
Type field, a chunk-specific Flag field, a Chunk Length field, and a
|
||||
Value field.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Chunk Type | Chunk Flags | Chunk Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| Chunk Value |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type chunkHeader struct {
|
||||
typ chunkType
|
||||
flags byte
|
||||
raw []byte
|
||||
}
|
||||
|
||||
const (
|
||||
chunkHeaderSize = 4
|
||||
)
|
||||
|
||||
// SCTP chunk header errors
|
||||
var (
|
||||
ErrChunkHeaderTooSmall = errors.New("raw is too small for a SCTP chunk")
|
||||
ErrChunkHeaderNotEnoughSpace = errors.New("not enough data left in SCTP packet to satisfy requested length")
|
||||
ErrChunkHeaderPaddingNonZero = errors.New("chunk padding is non-zero at offset")
|
||||
)
|
||||
|
||||
func (c *chunkHeader) unmarshal(raw []byte) error {
|
||||
if len(raw) < chunkHeaderSize {
|
||||
return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrChunkHeaderTooSmall, len(raw), chunkHeaderSize)
|
||||
}
|
||||
|
||||
c.typ = chunkType(raw[0])
|
||||
c.flags = raw[1]
|
||||
length := binary.BigEndian.Uint16(raw[2:])
|
||||
|
||||
// Length includes Chunk header
|
||||
valueLength := int(length - chunkHeaderSize)
|
||||
lengthAfterValue := len(raw) - (chunkHeaderSize + valueLength)
|
||||
|
||||
if lengthAfterValue < 0 {
|
||||
return fmt.Errorf("%w: remain %d req %d ", ErrChunkHeaderNotEnoughSpace, valueLength, len(raw)-chunkHeaderSize)
|
||||
} else if lengthAfterValue < 4 {
|
||||
// https://tools.ietf.org/html/rfc4960#section-3.2
|
||||
// The Chunk Length field does not count any chunk padding.
|
||||
// Chunks (including Type, Length, and Value fields) are padded out
|
||||
// by the sender with all zero bytes to be a multiple of 4 bytes
|
||||
// long. This padding MUST NOT be more than 3 bytes in total. The
|
||||
// Chunk Length value does not include terminating padding of the
|
||||
// chunk. However, it does include padding of any variable-length
|
||||
// parameter except the last parameter in the chunk. The receiver
|
||||
// MUST ignore the padding.
|
||||
for i := lengthAfterValue; i > 0; i-- {
|
||||
paddingOffset := chunkHeaderSize + valueLength + (i - 1)
|
||||
if raw[paddingOffset] != 0 {
|
||||
return fmt.Errorf("%w: %d ", ErrChunkHeaderPaddingNonZero, paddingOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.raw = raw[chunkHeaderSize : chunkHeaderSize+valueLength]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkHeader) marshal() ([]byte, error) {
|
||||
raw := make([]byte, 4+len(c.raw))
|
||||
|
||||
raw[0] = uint8(c.typ)
|
||||
raw[1] = c.flags
|
||||
binary.BigEndian.PutUint16(raw[2:], uint16(len(c.raw)+chunkHeaderSize))
|
||||
copy(raw[4:], c.raw)
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (c *chunkHeader) valueLength() int {
|
||||
return len(c.raw)
|
||||
}
|
||||
|
||||
// String makes chunkHeader printable
|
||||
func (c chunkHeader) String() string {
|
||||
return c.typ.String()
|
||||
}
|
||||
70
server/vendor/github.com/pion/sctp/chunktype.go
generated
vendored
Normal file
70
server/vendor/github.com/pion/sctp/chunktype.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import "fmt"
|
||||
|
||||
// chunkType is an enum for SCTP Chunk Type field
|
||||
// This field identifies the type of information contained in the
|
||||
// Chunk Value field.
|
||||
type chunkType uint8
|
||||
|
||||
// List of known chunkType enums
|
||||
const (
|
||||
ctPayloadData chunkType = 0
|
||||
ctInit chunkType = 1
|
||||
ctInitAck chunkType = 2
|
||||
ctSack chunkType = 3
|
||||
ctHeartbeat chunkType = 4
|
||||
ctHeartbeatAck chunkType = 5
|
||||
ctAbort chunkType = 6
|
||||
ctShutdown chunkType = 7
|
||||
ctShutdownAck chunkType = 8
|
||||
ctError chunkType = 9
|
||||
ctCookieEcho chunkType = 10
|
||||
ctCookieAck chunkType = 11
|
||||
ctCWR chunkType = 13
|
||||
ctShutdownComplete chunkType = 14
|
||||
ctReconfig chunkType = 130
|
||||
ctForwardTSN chunkType = 192
|
||||
)
|
||||
|
||||
func (c chunkType) String() string {
|
||||
switch c {
|
||||
case ctPayloadData:
|
||||
return "DATA"
|
||||
case ctInit:
|
||||
return "INIT"
|
||||
case ctInitAck:
|
||||
return "INIT-ACK"
|
||||
case ctSack:
|
||||
return "SACK"
|
||||
case ctHeartbeat:
|
||||
return "HEARTBEAT"
|
||||
case ctHeartbeatAck:
|
||||
return "HEARTBEAT-ACK"
|
||||
case ctAbort:
|
||||
return "ABORT"
|
||||
case ctShutdown:
|
||||
return "SHUTDOWN"
|
||||
case ctShutdownAck:
|
||||
return "SHUTDOWN-ACK"
|
||||
case ctError:
|
||||
return "ERROR"
|
||||
case ctCookieEcho:
|
||||
return "COOKIE-ECHO"
|
||||
case ctCookieAck:
|
||||
return "COOKIE-ACK"
|
||||
case ctCWR:
|
||||
return "ECNE" // Explicit Congestion Notification Echo
|
||||
case ctShutdownComplete:
|
||||
return "SHUTDOWN-COMPLETE"
|
||||
case ctReconfig:
|
||||
return "RECONFIG" // Re-configuration
|
||||
case ctForwardTSN:
|
||||
return "FORWARD-TSN"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown ChunkType: %d", c)
|
||||
}
|
||||
}
|
||||
22
server/vendor/github.com/pion/sctp/codecov.yml
generated
vendored
Normal file
22
server/vendor/github.com/pion/sctp/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# DO NOT EDIT THIS FILE
|
||||
#
|
||||
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Allow decreasing 2% of total coverage to avoid noise.
|
||||
threshold: 2%
|
||||
patch:
|
||||
default:
|
||||
target: 70%
|
||||
only_pulls: true
|
||||
|
||||
ignore:
|
||||
- "examples/*"
|
||||
- "examples/**/*"
|
||||
32
server/vendor/github.com/pion/sctp/control_queue.go
generated
vendored
Normal file
32
server/vendor/github.com/pion/sctp/control_queue.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
// control queue
|
||||
|
||||
type controlQueue struct {
|
||||
queue []*packet
|
||||
}
|
||||
|
||||
func newControlQueue() *controlQueue {
|
||||
return &controlQueue{queue: []*packet{}}
|
||||
}
|
||||
|
||||
func (q *controlQueue) push(c *packet) {
|
||||
q.queue = append(q.queue, c)
|
||||
}
|
||||
|
||||
func (q *controlQueue) pushAll(packets []*packet) {
|
||||
q.queue = append(q.queue, packets...)
|
||||
}
|
||||
|
||||
func (q *controlQueue) popAll() []*packet {
|
||||
packets := q.queue
|
||||
q.queue = []*packet{}
|
||||
return packets
|
||||
}
|
||||
|
||||
func (q *controlQueue) size() int {
|
||||
return len(q.queue)
|
||||
}
|
||||
101
server/vendor/github.com/pion/sctp/error_cause.go
generated
vendored
Normal file
101
server/vendor/github.com/pion/sctp/error_cause.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// errorCauseCode is a cause code that appears in either a ERROR or ABORT chunk
|
||||
type errorCauseCode uint16
|
||||
|
||||
type errorCause interface {
|
||||
unmarshal([]byte) error
|
||||
marshal() ([]byte, error)
|
||||
length() uint16
|
||||
String() string
|
||||
|
||||
errorCauseCode() errorCauseCode
|
||||
}
|
||||
|
||||
// Error and abort chunk errors
|
||||
var (
|
||||
ErrBuildErrorCaseHandle = errors.New("BuildErrorCause does not handle")
|
||||
)
|
||||
|
||||
// buildErrorCause delegates the building of a error cause from raw bytes to the correct structure
|
||||
func buildErrorCause(raw []byte) (errorCause, error) {
|
||||
var e errorCause
|
||||
|
||||
c := errorCauseCode(binary.BigEndian.Uint16(raw[0:]))
|
||||
switch c {
|
||||
case invalidMandatoryParameter:
|
||||
e = &errorCauseInvalidMandatoryParameter{}
|
||||
case unrecognizedChunkType:
|
||||
e = &errorCauseUnrecognizedChunkType{}
|
||||
case protocolViolation:
|
||||
e = &errorCauseProtocolViolation{}
|
||||
case userInitiatedAbort:
|
||||
e = &errorCauseUserInitiatedAbort{}
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", ErrBuildErrorCaseHandle, c.String())
|
||||
}
|
||||
|
||||
if err := e.unmarshal(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
const (
|
||||
invalidStreamIdentifier errorCauseCode = 1
|
||||
missingMandatoryParameter errorCauseCode = 2
|
||||
staleCookieError errorCauseCode = 3
|
||||
outOfResource errorCauseCode = 4
|
||||
unresolvableAddress errorCauseCode = 5
|
||||
unrecognizedChunkType errorCauseCode = 6
|
||||
invalidMandatoryParameter errorCauseCode = 7
|
||||
unrecognizedParameters errorCauseCode = 8
|
||||
noUserData errorCauseCode = 9
|
||||
cookieReceivedWhileShuttingDown errorCauseCode = 10
|
||||
restartOfAnAssociationWithNewAddresses errorCauseCode = 11
|
||||
userInitiatedAbort errorCauseCode = 12
|
||||
protocolViolation errorCauseCode = 13
|
||||
)
|
||||
|
||||
func (e errorCauseCode) String() string {
|
||||
switch e {
|
||||
case invalidStreamIdentifier:
|
||||
return "Invalid Stream Identifier"
|
||||
case missingMandatoryParameter:
|
||||
return "Missing Mandatory Parameter"
|
||||
case staleCookieError:
|
||||
return "Stale Cookie Error"
|
||||
case outOfResource:
|
||||
return "Out Of Resource"
|
||||
case unresolvableAddress:
|
||||
return "Unresolvable IP"
|
||||
case unrecognizedChunkType:
|
||||
return "Unrecognized Chunk Type"
|
||||
case invalidMandatoryParameter:
|
||||
return "Invalid Mandatory Parameter"
|
||||
case unrecognizedParameters:
|
||||
return "Unrecognized Parameters"
|
||||
case noUserData:
|
||||
return "No User Data"
|
||||
case cookieReceivedWhileShuttingDown:
|
||||
return "Cookie Received While Shutting Down"
|
||||
case restartOfAnAssociationWithNewAddresses:
|
||||
return "Restart Of An Association With New Addresses"
|
||||
case userInitiatedAbort:
|
||||
return "User Initiated Abort"
|
||||
case protocolViolation:
|
||||
return "Protocol Violation"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown CauseCode: %d", e)
|
||||
}
|
||||
}
|
||||
57
server/vendor/github.com/pion/sctp/error_cause_header.go
generated
vendored
Normal file
57
server/vendor/github.com/pion/sctp/error_cause_header.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// errorCauseHeader represents the shared header that is shared by all error causes
|
||||
type errorCauseHeader struct {
|
||||
code errorCauseCode
|
||||
len uint16
|
||||
raw []byte
|
||||
}
|
||||
|
||||
const (
|
||||
errorCauseHeaderLength = 4
|
||||
)
|
||||
|
||||
// ErrInvalidSCTPChunk is returned when an SCTP chunk is invalid
|
||||
var ErrInvalidSCTPChunk = errors.New("invalid SCTP chunk")
|
||||
|
||||
func (e *errorCauseHeader) marshal() ([]byte, error) {
|
||||
e.len = uint16(len(e.raw)) + uint16(errorCauseHeaderLength)
|
||||
raw := make([]byte, e.len)
|
||||
binary.BigEndian.PutUint16(raw[0:], uint16(e.code))
|
||||
binary.BigEndian.PutUint16(raw[2:], e.len)
|
||||
copy(raw[errorCauseHeaderLength:], e.raw)
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (e *errorCauseHeader) unmarshal(raw []byte) error {
|
||||
e.code = errorCauseCode(binary.BigEndian.Uint16(raw[0:]))
|
||||
e.len = binary.BigEndian.Uint16(raw[2:])
|
||||
if e.len < errorCauseHeaderLength || int(e.len) > len(raw) {
|
||||
return ErrInvalidSCTPChunk
|
||||
}
|
||||
valueLength := e.len - errorCauseHeaderLength
|
||||
e.raw = raw[errorCauseHeaderLength : errorCauseHeaderLength+valueLength]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *errorCauseHeader) length() uint16 {
|
||||
return e.len
|
||||
}
|
||||
|
||||
func (e *errorCauseHeader) errorCauseCode() errorCauseCode {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// String makes errorCauseHeader printable
|
||||
func (e errorCauseHeader) String() string {
|
||||
return e.code.String()
|
||||
}
|
||||
22
server/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go
generated
vendored
Normal file
22
server/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
// errorCauseInvalidMandatoryParameter represents an SCTP error cause
|
||||
type errorCauseInvalidMandatoryParameter struct {
|
||||
errorCauseHeader
|
||||
}
|
||||
|
||||
func (e *errorCauseInvalidMandatoryParameter) marshal() ([]byte, error) {
|
||||
return e.errorCauseHeader.marshal()
|
||||
}
|
||||
|
||||
func (e *errorCauseInvalidMandatoryParameter) unmarshal(raw []byte) error {
|
||||
return e.errorCauseHeader.unmarshal(raw)
|
||||
}
|
||||
|
||||
// String makes errorCauseInvalidMandatoryParameter printable
|
||||
func (e *errorCauseInvalidMandatoryParameter) String() string {
|
||||
return e.errorCauseHeader.String()
|
||||
}
|
||||
57
server/vendor/github.com/pion/sctp/error_cause_protocol_violation.go
generated
vendored
Normal file
57
server/vendor/github.com/pion/sctp/error_cause_protocol_violation.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
This error cause MAY be included in ABORT chunks that are sent
|
||||
because an SCTP endpoint detects a protocol violation of the peer
|
||||
that is not covered by the error causes described in Section 3.3.10.1
|
||||
to Section 3.3.10.12. An implementation MAY provide additional
|
||||
information specifying what kind of protocol violation has been
|
||||
detected.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cause Code=13 | Cause Length=Variable |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ Additional Information /
|
||||
\ \
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type errorCauseProtocolViolation struct {
|
||||
errorCauseHeader
|
||||
additionalInformation []byte
|
||||
}
|
||||
|
||||
// Abort chunk errors
|
||||
var (
|
||||
ErrProtocolViolationUnmarshal = errors.New("unable to unmarshal Protocol Violation error")
|
||||
)
|
||||
|
||||
func (e *errorCauseProtocolViolation) marshal() ([]byte, error) {
|
||||
e.raw = e.additionalInformation
|
||||
return e.errorCauseHeader.marshal()
|
||||
}
|
||||
|
||||
func (e *errorCauseProtocolViolation) unmarshal(raw []byte) error {
|
||||
err := e.errorCauseHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrProtocolViolationUnmarshal, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
e.additionalInformation = e.raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String makes errorCauseProtocolViolation printable
|
||||
func (e *errorCauseProtocolViolation) String() string {
|
||||
return fmt.Sprintf("%s: %s", e.errorCauseHeader, e.additionalInformation)
|
||||
}
|
||||
31
server/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go
generated
vendored
Normal file
31
server/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
// errorCauseUnrecognizedChunkType represents an SCTP error cause
|
||||
type errorCauseUnrecognizedChunkType struct {
|
||||
errorCauseHeader
|
||||
unrecognizedChunk []byte
|
||||
}
|
||||
|
||||
func (e *errorCauseUnrecognizedChunkType) marshal() ([]byte, error) {
|
||||
e.code = unrecognizedChunkType
|
||||
e.errorCauseHeader.raw = e.unrecognizedChunk
|
||||
return e.errorCauseHeader.marshal()
|
||||
}
|
||||
|
||||
func (e *errorCauseUnrecognizedChunkType) unmarshal(raw []byte) error {
|
||||
err := e.errorCauseHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.unrecognizedChunk = e.errorCauseHeader.raw
|
||||
return nil
|
||||
}
|
||||
|
||||
// String makes errorCauseUnrecognizedChunkType printable
|
||||
func (e *errorCauseUnrecognizedChunkType) String() string {
|
||||
return e.errorCauseHeader.String()
|
||||
}
|
||||
49
server/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go
generated
vendored
Normal file
49
server/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
This error cause MAY be included in ABORT chunks that are sent
|
||||
because of an upper-layer request. The upper layer can specify an
|
||||
Upper Layer Abort Reason that is transported by SCTP transparently
|
||||
and MAY be delivered to the upper-layer protocol at the peer.
|
||||
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Cause Code=12 | Cause Length=Variable |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ Upper Layer Abort Reason /
|
||||
\ \
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type errorCauseUserInitiatedAbort struct {
|
||||
errorCauseHeader
|
||||
upperLayerAbortReason []byte
|
||||
}
|
||||
|
||||
func (e *errorCauseUserInitiatedAbort) marshal() ([]byte, error) {
|
||||
e.code = userInitiatedAbort
|
||||
e.errorCauseHeader.raw = e.upperLayerAbortReason
|
||||
return e.errorCauseHeader.marshal()
|
||||
}
|
||||
|
||||
func (e *errorCauseUserInitiatedAbort) unmarshal(raw []byte) error {
|
||||
err := e.errorCauseHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.upperLayerAbortReason = e.errorCauseHeader.raw
|
||||
return nil
|
||||
}
|
||||
|
||||
// String makes errorCauseUserInitiatedAbort printable
|
||||
func (e *errorCauseUserInitiatedAbort) String() string {
|
||||
return fmt.Sprintf("%s: %s", e.errorCauseHeader.String(), e.upperLayerAbortReason)
|
||||
}
|
||||
227
server/vendor/github.com/pion/sctp/packet.go
generated
vendored
Normal file
227
server/vendor/github.com/pion/sctp/packet.go
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
// Create the crc32 table we'll use for the checksum
|
||||
var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) // nolint:gochecknoglobals
|
||||
|
||||
// Allocate and zero this data once.
|
||||
// We need to use it for the checksum and don't want to allocate/clear each time.
|
||||
var fourZeroes [4]byte // nolint:gochecknoglobals
|
||||
|
||||
/*
|
||||
Packet represents an SCTP packet, defined in https://tools.ietf.org/html/rfc4960#section-3
|
||||
An SCTP packet is composed of a common header and chunks. A chunk
|
||||
contains either control information or user data.
|
||||
|
||||
SCTP Packet Format
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Common Header |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Chunk #1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ... |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Chunk #n |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
SCTP Common Header Format
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Value Number | Destination Value Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Verification Tag |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type packet struct {
|
||||
sourcePort uint16
|
||||
destinationPort uint16
|
||||
verificationTag uint32
|
||||
chunks []chunk
|
||||
}
|
||||
|
||||
const (
|
||||
packetHeaderSize = 12
|
||||
)
|
||||
|
||||
// SCTP packet errors
|
||||
var (
|
||||
ErrPacketRawTooSmall = errors.New("raw is smaller than the minimum length for a SCTP packet")
|
||||
ErrParseSCTPChunkNotEnoughData = errors.New("unable to parse SCTP chunk, not enough data for complete header")
|
||||
ErrUnmarshalUnknownChunkType = errors.New("failed to unmarshal, contains unknown chunk type")
|
||||
ErrChecksumMismatch = errors.New("checksum mismatch theirs")
|
||||
)
|
||||
|
||||
func (p *packet) unmarshal(doChecksum bool, raw []byte) error {
|
||||
if len(raw) < packetHeaderSize {
|
||||
return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrPacketRawTooSmall, len(raw), packetHeaderSize)
|
||||
}
|
||||
|
||||
offset := packetHeaderSize
|
||||
|
||||
// Check if doing CRC32c is required.
|
||||
// Without having SCTP AUTH implemented, this depends only on the type
|
||||
// og the first chunk.
|
||||
if offset+chunkHeaderSize <= len(raw) {
|
||||
switch chunkType(raw[offset]) {
|
||||
case ctInit, ctCookieEcho:
|
||||
doChecksum = true
|
||||
default:
|
||||
}
|
||||
}
|
||||
theirChecksum := binary.LittleEndian.Uint32(raw[8:])
|
||||
if theirChecksum != 0 || doChecksum {
|
||||
ourChecksum := generatePacketChecksum(raw)
|
||||
if theirChecksum != ourChecksum {
|
||||
return fmt.Errorf("%w: %d ours: %d", ErrChecksumMismatch, theirChecksum, ourChecksum)
|
||||
}
|
||||
}
|
||||
|
||||
p.sourcePort = binary.BigEndian.Uint16(raw[0:])
|
||||
p.destinationPort = binary.BigEndian.Uint16(raw[2:])
|
||||
p.verificationTag = binary.BigEndian.Uint32(raw[4:])
|
||||
|
||||
for {
|
||||
// Exact match, no more chunks
|
||||
if offset == len(raw) {
|
||||
break
|
||||
} else if offset+chunkHeaderSize > len(raw) {
|
||||
return fmt.Errorf("%w: offset %d remaining %d", ErrParseSCTPChunkNotEnoughData, offset, len(raw))
|
||||
}
|
||||
|
||||
var c chunk
|
||||
switch chunkType(raw[offset]) {
|
||||
case ctInit:
|
||||
c = &chunkInit{}
|
||||
case ctInitAck:
|
||||
c = &chunkInitAck{}
|
||||
case ctAbort:
|
||||
c = &chunkAbort{}
|
||||
case ctCookieEcho:
|
||||
c = &chunkCookieEcho{}
|
||||
case ctCookieAck:
|
||||
c = &chunkCookieAck{}
|
||||
case ctHeartbeat:
|
||||
c = &chunkHeartbeat{}
|
||||
case ctPayloadData:
|
||||
c = &chunkPayloadData{}
|
||||
case ctSack:
|
||||
c = &chunkSelectiveAck{}
|
||||
case ctReconfig:
|
||||
c = &chunkReconfig{}
|
||||
case ctForwardTSN:
|
||||
c = &chunkForwardTSN{}
|
||||
case ctError:
|
||||
c = &chunkError{}
|
||||
case ctShutdown:
|
||||
c = &chunkShutdown{}
|
||||
case ctShutdownAck:
|
||||
c = &chunkShutdownAck{}
|
||||
case ctShutdownComplete:
|
||||
c = &chunkShutdownComplete{}
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrUnmarshalUnknownChunkType, chunkType(raw[offset]).String())
|
||||
}
|
||||
|
||||
if err := c.unmarshal(raw[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.chunks = append(p.chunks, c)
|
||||
chunkValuePadding := getPadding(c.valueLength())
|
||||
offset += chunkHeaderSize + c.valueLength() + chunkValuePadding
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *packet) marshal(doChecksum bool) ([]byte, error) {
|
||||
raw := make([]byte, packetHeaderSize)
|
||||
|
||||
// Populate static headers
|
||||
// 8-12 is Checksum which will be populated when packet is complete
|
||||
binary.BigEndian.PutUint16(raw[0:], p.sourcePort)
|
||||
binary.BigEndian.PutUint16(raw[2:], p.destinationPort)
|
||||
binary.BigEndian.PutUint32(raw[4:], p.verificationTag)
|
||||
|
||||
// Populate chunks
|
||||
for _, c := range p.chunks {
|
||||
chunkRaw, err := c.marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw = append(raw, chunkRaw...)
|
||||
|
||||
paddingNeeded := getPadding(len(raw))
|
||||
if paddingNeeded != 0 {
|
||||
raw = append(raw, make([]byte, paddingNeeded)...)
|
||||
}
|
||||
}
|
||||
|
||||
if doChecksum {
|
||||
// golang CRC32C uses reflected input and reflected output, the
|
||||
// net result of this is to have the bytes flipped compared to
|
||||
// the non reflected variant that the spec expects.
|
||||
//
|
||||
// Use LittleEndian.PutUint32 to avoid flipping the bytes in to
|
||||
// the spec compliant checksum order
|
||||
binary.LittleEndian.PutUint32(raw[8:], generatePacketChecksum(raw))
|
||||
}
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func generatePacketChecksum(raw []byte) (sum uint32) {
|
||||
// Fastest way to do a crc32 without allocating.
|
||||
sum = crc32.Update(sum, castagnoliTable, raw[0:8])
|
||||
sum = crc32.Update(sum, castagnoliTable, fourZeroes[:])
|
||||
sum = crc32.Update(sum, castagnoliTable, raw[12:])
|
||||
return sum
|
||||
}
|
||||
|
||||
// String makes packet printable
|
||||
func (p *packet) String() string {
|
||||
format := `Packet:
|
||||
sourcePort: %d
|
||||
destinationPort: %d
|
||||
verificationTag: %d
|
||||
`
|
||||
res := fmt.Sprintf(format,
|
||||
p.sourcePort,
|
||||
p.destinationPort,
|
||||
p.verificationTag,
|
||||
)
|
||||
for i, chunk := range p.chunks {
|
||||
res += fmt.Sprintf("Chunk %d:\n %s", i, chunk)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// TryMarshalUnmarshal attempts to marshal and unmarshal a message. Added for fuzzing.
|
||||
func TryMarshalUnmarshal(msg []byte) int {
|
||||
p := &packet{}
|
||||
err := p.unmarshal(false, msg)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
_, err = p.marshal(false)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
46
server/vendor/github.com/pion/sctp/param.go
generated
vendored
Normal file
46
server/vendor/github.com/pion/sctp/param.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type param interface {
|
||||
marshal() ([]byte, error)
|
||||
length() int
|
||||
}
|
||||
|
||||
// ErrParamTypeUnhandled is returned if unknown parameter type is specified.
|
||||
var ErrParamTypeUnhandled = errors.New("unhandled ParamType")
|
||||
|
||||
func buildParam(t paramType, rawParam []byte) (param, error) {
|
||||
switch t {
|
||||
case forwardTSNSupp:
|
||||
return (¶mForwardTSNSupported{}).unmarshal(rawParam)
|
||||
case supportedExt:
|
||||
return (¶mSupportedExtensions{}).unmarshal(rawParam)
|
||||
case ecnCapable:
|
||||
return (¶mECNCapable{}).unmarshal(rawParam)
|
||||
case random:
|
||||
return (¶mRandom{}).unmarshal(rawParam)
|
||||
case reqHMACAlgo:
|
||||
return (¶mRequestedHMACAlgorithm{}).unmarshal(rawParam)
|
||||
case chunkList:
|
||||
return (¶mChunkList{}).unmarshal(rawParam)
|
||||
case stateCookie:
|
||||
return (¶mStateCookie{}).unmarshal(rawParam)
|
||||
case heartbeatInfo:
|
||||
return (¶mHeartbeatInfo{}).unmarshal(rawParam)
|
||||
case outSSNResetReq:
|
||||
return (¶mOutgoingResetRequest{}).unmarshal(rawParam)
|
||||
case reconfigResp:
|
||||
return (¶mReconfigResponse{}).unmarshal(rawParam)
|
||||
case zeroChecksumAcceptable:
|
||||
return (¶mZeroChecksumAcceptable{}).unmarshal(rawParam)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %v", ErrParamTypeUnhandled, t)
|
||||
}
|
||||
}
|
||||
31
server/vendor/github.com/pion/sctp/param_chunk_list.go
generated
vendored
Normal file
31
server/vendor/github.com/pion/sctp/param_chunk_list.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type paramChunkList struct {
|
||||
paramHeader
|
||||
chunkTypes []chunkType
|
||||
}
|
||||
|
||||
func (c *paramChunkList) marshal() ([]byte, error) {
|
||||
c.typ = chunkList
|
||||
c.raw = make([]byte, len(c.chunkTypes))
|
||||
for i, t := range c.chunkTypes {
|
||||
c.raw[i] = byte(t)
|
||||
}
|
||||
|
||||
return c.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (c *paramChunkList) unmarshal(raw []byte) (param, error) {
|
||||
err := c.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, t := range c.raw {
|
||||
c.chunkTypes = append(c.chunkTypes, chunkType(t))
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
22
server/vendor/github.com/pion/sctp/param_ecn_capable.go
generated
vendored
Normal file
22
server/vendor/github.com/pion/sctp/param_ecn_capable.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type paramECNCapable struct {
|
||||
paramHeader
|
||||
}
|
||||
|
||||
func (r *paramECNCapable) marshal() ([]byte, error) {
|
||||
r.typ = ecnCapable
|
||||
r.raw = []byte{}
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramECNCapable) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
31
server/vendor/github.com/pion/sctp/param_forward_tsn_supported.go
generated
vendored
Normal file
31
server/vendor/github.com/pion/sctp/param_forward_tsn_supported.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
// At the initialization of the association, the sender of the INIT or
|
||||
// INIT ACK chunk MAY include this OPTIONAL parameter to inform its peer
|
||||
// that it is able to support the Forward TSN chunk
|
||||
//
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Parameter Type = 49152 | Parameter Length = 4 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type paramForwardTSNSupported struct {
|
||||
paramHeader
|
||||
}
|
||||
|
||||
func (f *paramForwardTSNSupported) marshal() ([]byte, error) {
|
||||
f.typ = forwardTSNSupp
|
||||
f.raw = []byte{}
|
||||
return f.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (f *paramForwardTSNSupported) unmarshal(raw []byte) (param, error) {
|
||||
err := f.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
24
server/vendor/github.com/pion/sctp/param_heartbeat_info.go
generated
vendored
Normal file
24
server/vendor/github.com/pion/sctp/param_heartbeat_info.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type paramHeartbeatInfo struct {
|
||||
paramHeader
|
||||
heartbeatInformation []byte
|
||||
}
|
||||
|
||||
func (h *paramHeartbeatInfo) marshal() ([]byte, error) {
|
||||
h.typ = heartbeatInfo
|
||||
h.raw = h.heartbeatInformation
|
||||
return h.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (h *paramHeartbeatInfo) unmarshal(raw []byte) (param, error) {
|
||||
err := h.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.heartbeatInformation = h.raw
|
||||
return h, nil
|
||||
}
|
||||
94
server/vendor/github.com/pion/sctp/param_outgoing_reset_request.go
generated
vendored
Normal file
94
server/vendor/github.com/pion/sctp/param_outgoing_reset_request.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
paramOutgoingResetRequestStreamIdentifiersOffset = 12
|
||||
)
|
||||
|
||||
// This parameter is used by the sender to request the reset of some or
|
||||
// all outgoing streams.
|
||||
// 0 1 2 3
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Parameter Type = 13 | Parameter Length = 16 + 2 * N |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Re-configuration Request Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Re-configuration Response Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Sender's Last Assigned TSN |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Stream Number 1 (optional) | Stream Number 2 (optional) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// / ...... /
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Stream Number N-1 (optional) | Stream Number N (optional) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type paramOutgoingResetRequest struct {
|
||||
paramHeader
|
||||
// reconfigRequestSequenceNumber is used to identify the request. It is a monotonically
|
||||
// increasing number that is initialized to the same value as the
|
||||
// initial TSN. It is increased by 1 whenever sending a new Re-
|
||||
// configuration Request Parameter.
|
||||
reconfigRequestSequenceNumber uint32
|
||||
// When this Outgoing SSN Reset Request Parameter is sent in response
|
||||
// to an Incoming SSN Reset Request Parameter, this parameter is also
|
||||
// an implicit response to the incoming request. This field then
|
||||
// holds the Re-configuration Request Sequence Number of the incoming
|
||||
// request. In other cases, it holds the next expected
|
||||
// Re-configuration Request Sequence Number minus 1.
|
||||
reconfigResponseSequenceNumber uint32
|
||||
// This value holds the next TSN minus 1 -- in other words, the last
|
||||
// TSN that this sender assigned.
|
||||
senderLastTSN uint32
|
||||
// This optional field, if included, is used to indicate specific
|
||||
// streams that are to be reset. If no streams are listed, then all
|
||||
// streams are to be reset.
|
||||
streamIdentifiers []uint16
|
||||
}
|
||||
|
||||
// Outgoing reset request parameter errors
|
||||
var (
|
||||
ErrSSNResetRequestParamTooShort = errors.New("outgoing SSN reset request parameter too short")
|
||||
)
|
||||
|
||||
func (r *paramOutgoingResetRequest) marshal() ([]byte, error) {
|
||||
r.typ = outSSNResetReq
|
||||
r.raw = make([]byte, paramOutgoingResetRequestStreamIdentifiersOffset+2*len(r.streamIdentifiers))
|
||||
binary.BigEndian.PutUint32(r.raw, r.reconfigRequestSequenceNumber)
|
||||
binary.BigEndian.PutUint32(r.raw[4:], r.reconfigResponseSequenceNumber)
|
||||
binary.BigEndian.PutUint32(r.raw[8:], r.senderLastTSN)
|
||||
for i, sID := range r.streamIdentifiers {
|
||||
binary.BigEndian.PutUint16(r.raw[paramOutgoingResetRequestStreamIdentifiersOffset+2*i:], sID)
|
||||
}
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramOutgoingResetRequest) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.raw) < paramOutgoingResetRequestStreamIdentifiersOffset {
|
||||
return nil, ErrSSNResetRequestParamTooShort
|
||||
}
|
||||
r.reconfigRequestSequenceNumber = binary.BigEndian.Uint32(r.raw)
|
||||
r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw[4:])
|
||||
r.senderLastTSN = binary.BigEndian.Uint32(r.raw[8:])
|
||||
|
||||
lim := (len(r.raw) - paramOutgoingResetRequestStreamIdentifiersOffset) / 2
|
||||
r.streamIdentifiers = make([]uint16, lim)
|
||||
for i := 0; i < lim; i++ {
|
||||
r.streamIdentifiers[i] = binary.BigEndian.Uint16(r.raw[paramOutgoingResetRequestStreamIdentifiersOffset+2*i:])
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
24
server/vendor/github.com/pion/sctp/param_random.go
generated
vendored
Normal file
24
server/vendor/github.com/pion/sctp/param_random.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type paramRandom struct {
|
||||
paramHeader
|
||||
randomData []byte
|
||||
}
|
||||
|
||||
func (r *paramRandom) marshal() ([]byte, error) {
|
||||
r.typ = random
|
||||
r.raw = r.randomData
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramRandom) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.randomData = r.raw
|
||||
return r, nil
|
||||
}
|
||||
98
server/vendor/github.com/pion/sctp/param_reconfig_response.go
generated
vendored
Normal file
98
server/vendor/github.com/pion/sctp/param_reconfig_response.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// This parameter is used by the receiver of a Re-configuration Request
|
||||
// Parameter to respond to the request.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Parameter Type = 16 | Parameter Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Re-configuration Response Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Result |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Sender's Next TSN (optional) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Receiver's Next TSN (optional) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type paramReconfigResponse struct {
|
||||
paramHeader
|
||||
// This value is copied from the request parameter and is used by the
|
||||
// receiver of the Re-configuration Response Parameter to tie the
|
||||
// response to the request.
|
||||
reconfigResponseSequenceNumber uint32
|
||||
// This value describes the result of the processing of the request.
|
||||
result reconfigResult
|
||||
}
|
||||
|
||||
type reconfigResult uint32
|
||||
|
||||
const (
|
||||
reconfigResultSuccessNOP reconfigResult = 0
|
||||
reconfigResultSuccessPerformed reconfigResult = 1
|
||||
reconfigResultDenied reconfigResult = 2
|
||||
reconfigResultErrorWrongSSN reconfigResult = 3
|
||||
reconfigResultErrorRequestAlreadyInProgress reconfigResult = 4
|
||||
reconfigResultErrorBadSequenceNumber reconfigResult = 5
|
||||
reconfigResultInProgress reconfigResult = 6
|
||||
)
|
||||
|
||||
// Reconfiguration response errors
|
||||
var (
|
||||
ErrReconfigRespParamTooShort = errors.New("reconfig response parameter too short")
|
||||
)
|
||||
|
||||
func (t reconfigResult) String() string {
|
||||
switch t {
|
||||
case reconfigResultSuccessNOP:
|
||||
return "0: Success - Nothing to do"
|
||||
case reconfigResultSuccessPerformed:
|
||||
return "1: Success - Performed"
|
||||
case reconfigResultDenied:
|
||||
return "2: Denied"
|
||||
case reconfigResultErrorWrongSSN:
|
||||
return "3: Error - Wrong SSN"
|
||||
case reconfigResultErrorRequestAlreadyInProgress:
|
||||
return "4: Error - Request already in progress"
|
||||
case reconfigResultErrorBadSequenceNumber:
|
||||
return "5: Error - Bad Sequence Number"
|
||||
case reconfigResultInProgress:
|
||||
return "6: In progress"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown reconfigResult: %d", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *paramReconfigResponse) marshal() ([]byte, error) {
|
||||
r.typ = reconfigResp
|
||||
r.raw = make([]byte, 8)
|
||||
binary.BigEndian.PutUint32(r.raw, r.reconfigResponseSequenceNumber)
|
||||
binary.BigEndian.PutUint32(r.raw[4:], uint32(r.result))
|
||||
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramReconfigResponse) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.raw) < 8 {
|
||||
return nil, ErrReconfigRespParamTooShort
|
||||
}
|
||||
r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw)
|
||||
r.result = reconfigResult(binary.BigEndian.Uint32(r.raw[4:]))
|
||||
|
||||
return r, nil
|
||||
}
|
||||
84
server/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go
generated
vendored
Normal file
84
server/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type hmacAlgorithm uint16
|
||||
|
||||
const (
|
||||
hmacResv1 hmacAlgorithm = 0
|
||||
hmacSHA128 hmacAlgorithm = 1
|
||||
hmacResv2 hmacAlgorithm = 2
|
||||
hmacSHA256 hmacAlgorithm = 3
|
||||
)
|
||||
|
||||
// ErrInvalidAlgorithmType is returned if unknown auth algorithm is specified.
|
||||
var ErrInvalidAlgorithmType = errors.New("invalid algorithm type")
|
||||
|
||||
// ErrInvalidChunkLength is returned if the chunk length is invalid.
|
||||
var ErrInvalidChunkLength = errors.New("invalid chunk length")
|
||||
|
||||
func (c hmacAlgorithm) String() string {
|
||||
switch c {
|
||||
case hmacResv1:
|
||||
return "HMAC Reserved (0x00)"
|
||||
case hmacSHA128:
|
||||
return "HMAC SHA-128"
|
||||
case hmacResv2:
|
||||
return "HMAC Reserved (0x02)"
|
||||
case hmacSHA256:
|
||||
return "HMAC SHA-256"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown HMAC Algorithm type: %d", c)
|
||||
}
|
||||
}
|
||||
|
||||
type paramRequestedHMACAlgorithm struct {
|
||||
paramHeader
|
||||
availableAlgorithms []hmacAlgorithm
|
||||
}
|
||||
|
||||
func (r *paramRequestedHMACAlgorithm) marshal() ([]byte, error) {
|
||||
r.typ = reqHMACAlgo
|
||||
r.raw = make([]byte, len(r.availableAlgorithms)*2)
|
||||
i := 0
|
||||
for _, a := range r.availableAlgorithms {
|
||||
binary.BigEndian.PutUint16(r.raw[i:], uint16(a))
|
||||
i += 2
|
||||
}
|
||||
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramRequestedHMACAlgorithm) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.raw)%2 == 1 {
|
||||
return nil, ErrInvalidChunkLength
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(r.raw) {
|
||||
a := hmacAlgorithm(binary.BigEndian.Uint16(r.raw[i:]))
|
||||
switch a {
|
||||
case hmacSHA128:
|
||||
fallthrough
|
||||
case hmacSHA256:
|
||||
r.availableAlgorithms = append(r.availableAlgorithms, a)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %v", ErrInvalidAlgorithmType, a)
|
||||
}
|
||||
|
||||
i += 2
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
49
server/vendor/github.com/pion/sctp/param_state_cookie.go
generated
vendored
Normal file
49
server/vendor/github.com/pion/sctp/param_state_cookie.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type paramStateCookie struct {
|
||||
paramHeader
|
||||
cookie []byte
|
||||
}
|
||||
|
||||
func newRandomStateCookie() (*paramStateCookie, error) {
|
||||
randCookie := make([]byte, 32)
|
||||
_, err := rand.Read(randCookie)
|
||||
// crypto/rand.Read returns n == len(b) if and only if err == nil.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := ¶mStateCookie{
|
||||
cookie: randCookie,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *paramStateCookie) marshal() ([]byte, error) {
|
||||
s.typ = stateCookie
|
||||
s.raw = s.cookie
|
||||
return s.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (s *paramStateCookie) unmarshal(raw []byte) (param, error) {
|
||||
err := s.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.cookie = s.raw
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// String makes paramStateCookie printable
|
||||
func (s *paramStateCookie) String() string {
|
||||
return fmt.Sprintf("%s: %s", s.paramHeader, s.cookie)
|
||||
}
|
||||
32
server/vendor/github.com/pion/sctp/param_supported_extensions.go
generated
vendored
Normal file
32
server/vendor/github.com/pion/sctp/param_supported_extensions.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type paramSupportedExtensions struct {
|
||||
paramHeader
|
||||
ChunkTypes []chunkType
|
||||
}
|
||||
|
||||
func (s *paramSupportedExtensions) marshal() ([]byte, error) {
|
||||
s.typ = supportedExt
|
||||
s.raw = make([]byte, len(s.ChunkTypes))
|
||||
for i, c := range s.ChunkTypes {
|
||||
s.raw[i] = byte(c)
|
||||
}
|
||||
|
||||
return s.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (s *paramSupportedExtensions) unmarshal(raw []byte) (param, error) {
|
||||
err := s.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, t := range s.raw {
|
||||
s.ChunkTypes = append(s.ChunkTypes, chunkType(t))
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
56
server/vendor/github.com/pion/sctp/param_zero_checksum.go
generated
vendored
Normal file
56
server/vendor/github.com/pion/sctp/param_zero_checksum.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// This parameter is used to inform the receiver that a sender is willing to
|
||||
// accept zero as checksum if some other error detection method is used
|
||||
// instead.
|
||||
//
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x8001 (suggested) | Length = 8 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Error Detection Method Identifier (EDMID) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
type paramZeroChecksumAcceptable struct {
|
||||
paramHeader
|
||||
// The Error Detection Method Identifier (EDMID) specifies an alternate
|
||||
// error detection method the sender of this parameter is willing to use for
|
||||
// received packets.
|
||||
edmid uint32
|
||||
}
|
||||
|
||||
// Zero Checksum parameter error
|
||||
var (
|
||||
ErrZeroChecksumParamTooShort = errors.New("zero checksum parameter too short")
|
||||
)
|
||||
|
||||
const (
|
||||
dtlsErrorDetectionMethod uint32 = 1
|
||||
)
|
||||
|
||||
func (r *paramZeroChecksumAcceptable) marshal() ([]byte, error) {
|
||||
r.typ = zeroChecksumAcceptable
|
||||
r.raw = make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(r.raw, r.edmid)
|
||||
return r.paramHeader.marshal()
|
||||
}
|
||||
|
||||
func (r *paramZeroChecksumAcceptable) unmarshal(raw []byte) (param, error) {
|
||||
err := r.paramHeader.unmarshal(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(r.raw) < 4 {
|
||||
return nil, ErrZeroChecksumParamTooShort
|
||||
}
|
||||
r.edmid = binary.BigEndian.Uint32(r.raw)
|
||||
return r, nil
|
||||
}
|
||||
101
server/vendor/github.com/pion/sctp/paramheader.go
generated
vendored
Normal file
101
server/vendor/github.com/pion/sctp/paramheader.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type paramHeaderUnrecognizedAction byte
|
||||
|
||||
type paramHeader struct {
|
||||
typ paramType
|
||||
unrecognizedAction paramHeaderUnrecognizedAction
|
||||
len int
|
||||
raw []byte
|
||||
}
|
||||
|
||||
/*
|
||||
The Parameter Types are encoded such that the highest-order 2 bits specify
|
||||
the action that is taken if the processing endpoint does not recognize the
|
||||
Parameter Type.
|
||||
|
||||
00 - Stop processing this parameter and do not process any further parameters within this chunk.
|
||||
|
||||
01 - Stop processing this parameter, do not process any further parameters within this chunk, and
|
||||
report the unrecognized parameter, as described in Section 3.2.2.
|
||||
|
||||
10 - Skip this parameter and continue processing.
|
||||
|
||||
11 - Skip this parameter and continue processing, but report the unrecognized
|
||||
parameter, as described in Section 3.2.2.
|
||||
|
||||
https://www.rfc-editor.org/rfc/rfc9260.html#section-3.2.1
|
||||
*/
|
||||
|
||||
const (
|
||||
paramHeaderUnrecognizedActionMask = 0b11000000
|
||||
paramHeaderUnrecognizedActionStop paramHeaderUnrecognizedAction = 0b00000000
|
||||
paramHeaderUnrecognizedActionStopAndReport paramHeaderUnrecognizedAction = 0b01000000
|
||||
paramHeaderUnrecognizedActionSkip paramHeaderUnrecognizedAction = 0b10000000
|
||||
paramHeaderUnrecognizedActionSkipAndReport paramHeaderUnrecognizedAction = 0b11000000
|
||||
|
||||
paramHeaderLength = 4
|
||||
)
|
||||
|
||||
// Parameter header parse errors
|
||||
var (
|
||||
ErrParamHeaderTooShort = errors.New("param header too short")
|
||||
ErrParamHeaderSelfReportedLengthShorter = errors.New("param self reported length is shorter than header length")
|
||||
ErrParamHeaderSelfReportedLengthLonger = errors.New("param self reported length is longer than header length")
|
||||
ErrParamHeaderParseFailed = errors.New("failed to parse param type")
|
||||
)
|
||||
|
||||
func (p *paramHeader) marshal() ([]byte, error) {
|
||||
paramLengthPlusHeader := paramHeaderLength + len(p.raw)
|
||||
|
||||
rawParam := make([]byte, paramLengthPlusHeader)
|
||||
binary.BigEndian.PutUint16(rawParam[0:], uint16(p.typ))
|
||||
binary.BigEndian.PutUint16(rawParam[2:], uint16(paramLengthPlusHeader))
|
||||
copy(rawParam[paramHeaderLength:], p.raw)
|
||||
|
||||
return rawParam, nil
|
||||
}
|
||||
|
||||
func (p *paramHeader) unmarshal(raw []byte) error {
|
||||
if len(raw) < paramHeaderLength {
|
||||
return ErrParamHeaderTooShort
|
||||
}
|
||||
|
||||
paramLengthPlusHeader := binary.BigEndian.Uint16(raw[2:])
|
||||
if int(paramLengthPlusHeader) < paramHeaderLength {
|
||||
return fmt.Errorf("%w: param self reported length (%d) shorter than header length (%d)", ErrParamHeaderSelfReportedLengthShorter, int(paramLengthPlusHeader), paramHeaderLength)
|
||||
}
|
||||
if len(raw) < int(paramLengthPlusHeader) {
|
||||
return fmt.Errorf("%w: param length (%d) shorter than its self reported length (%d)", ErrParamHeaderSelfReportedLengthLonger, len(raw), int(paramLengthPlusHeader))
|
||||
}
|
||||
|
||||
typ, err := parseParamType(raw[0:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrParamHeaderParseFailed, err) //nolint:errorlint
|
||||
}
|
||||
p.typ = typ
|
||||
p.unrecognizedAction = paramHeaderUnrecognizedAction(raw[0] & paramHeaderUnrecognizedActionMask)
|
||||
p.raw = raw[paramHeaderLength:paramLengthPlusHeader]
|
||||
p.len = int(paramLengthPlusHeader)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *paramHeader) length() int {
|
||||
return p.len
|
||||
}
|
||||
|
||||
// String makes paramHeader printable
|
||||
func (p paramHeader) String() string {
|
||||
return fmt.Sprintf("%s (%d): %s", p.typ, p.len, hex.Dump(p.raw))
|
||||
}
|
||||
119
server/vendor/github.com/pion/sctp/paramtype.go
generated
vendored
Normal file
119
server/vendor/github.com/pion/sctp/paramtype.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// paramType represents a SCTP INIT/INITACK parameter
|
||||
type paramType uint16
|
||||
|
||||
const (
|
||||
heartbeatInfo paramType = 1 // Heartbeat Info [RFC4960]
|
||||
ipV4Addr paramType = 5 // IPv4 IP [RFC4960]
|
||||
ipV6Addr paramType = 6 // IPv6 IP [RFC4960]
|
||||
stateCookie paramType = 7 // State Cookie [RFC4960]
|
||||
unrecognizedParam paramType = 8 // Unrecognized Parameters [RFC4960]
|
||||
cookiePreservative paramType = 9 // Cookie Preservative [RFC4960]
|
||||
hostNameAddr paramType = 11 // Host Name IP [RFC4960]
|
||||
supportedAddrTypes paramType = 12 // Supported IP Types [RFC4960]
|
||||
outSSNResetReq paramType = 13 // Outgoing SSN Reset Request Parameter [RFC6525]
|
||||
incSSNResetReq paramType = 14 // Incoming SSN Reset Request Parameter [RFC6525]
|
||||
ssnTSNResetReq paramType = 15 // SSN/TSN Reset Request Parameter [RFC6525]
|
||||
reconfigResp paramType = 16 // Re-configuration Response Parameter [RFC6525]
|
||||
addOutStreamsReq paramType = 17 // Add Outgoing Streams Request Parameter [RFC6525]
|
||||
addIncStreamsReq paramType = 18 // Add Incoming Streams Request Parameter [RFC6525]
|
||||
ecnCapable paramType = 32768 // ECN Capable (0x8000) [RFC2960]
|
||||
zeroChecksumAcceptable paramType = 32769 // Zero Checksum Acceptable [draft-ietf-tsvwg-sctp-zero-checksum-00]
|
||||
random paramType = 32770 // Random (0x8002) [RFC4805]
|
||||
chunkList paramType = 32771 // Chunk List (0x8003) [RFC4895]
|
||||
reqHMACAlgo paramType = 32772 // Requested HMAC Algorithm Parameter (0x8004) [RFC4895]
|
||||
padding paramType = 32773 // Padding (0x8005)
|
||||
supportedExt paramType = 32776 // Supported Extensions (0x8008) [RFC5061]
|
||||
forwardTSNSupp paramType = 49152 // Forward TSN supported (0xC000) [RFC3758]
|
||||
addIPAddr paramType = 49153 // Add IP IP (0xC001) [RFC5061]
|
||||
delIPAddr paramType = 49154 // Delete IP IP (0xC002) [RFC5061]
|
||||
errClauseInd paramType = 49155 // Error Cause Indication (0xC003) [RFC5061]
|
||||
setPriAddr paramType = 49156 // Set Primary IP (0xC004) [RFC5061]
|
||||
successInd paramType = 49157 // Success Indication (0xC005) [RFC5061]
|
||||
adaptLayerInd paramType = 49158 // Adaptation Layer Indication (0xC006) [RFC5061]
|
||||
)
|
||||
|
||||
// Parameter packet errors
|
||||
var (
|
||||
ErrParamPacketTooShort = errors.New("packet to short")
|
||||
)
|
||||
|
||||
func parseParamType(raw []byte) (paramType, error) {
|
||||
if len(raw) < 2 {
|
||||
return paramType(0), ErrParamPacketTooShort
|
||||
}
|
||||
return paramType(binary.BigEndian.Uint16(raw)), nil
|
||||
}
|
||||
|
||||
func (p paramType) String() string {
|
||||
switch p {
|
||||
case heartbeatInfo:
|
||||
return "Heartbeat Info"
|
||||
case ipV4Addr:
|
||||
return "IPv4 IP"
|
||||
case ipV6Addr:
|
||||
return "IPv6 IP"
|
||||
case stateCookie:
|
||||
return "State Cookie"
|
||||
case unrecognizedParam:
|
||||
return "Unrecognized Parameters"
|
||||
case cookiePreservative:
|
||||
return "Cookie Preservative"
|
||||
case hostNameAddr:
|
||||
return "Host Name IP"
|
||||
case supportedAddrTypes:
|
||||
return "Supported IP Types"
|
||||
case outSSNResetReq:
|
||||
return "Outgoing SSN Reset Request Parameter"
|
||||
case incSSNResetReq:
|
||||
return "Incoming SSN Reset Request Parameter"
|
||||
case ssnTSNResetReq:
|
||||
return "SSN/TSN Reset Request Parameter"
|
||||
case reconfigResp:
|
||||
return "Re-configuration Response Parameter"
|
||||
case addOutStreamsReq:
|
||||
return "Add Outgoing Streams Request Parameter"
|
||||
case addIncStreamsReq:
|
||||
return "Add Incoming Streams Request Parameter"
|
||||
case ecnCapable:
|
||||
return "ECN Capable"
|
||||
case zeroChecksumAcceptable:
|
||||
return "Zero Checksum Acceptable"
|
||||
case random:
|
||||
return "Random"
|
||||
case chunkList:
|
||||
return "Chunk List"
|
||||
case reqHMACAlgo:
|
||||
return "Requested HMAC Algorithm Parameter"
|
||||
case padding:
|
||||
return "Padding"
|
||||
case supportedExt:
|
||||
return "Supported Extensions"
|
||||
case forwardTSNSupp:
|
||||
return "Forward TSN supported"
|
||||
case addIPAddr:
|
||||
return "Add IP IP"
|
||||
case delIPAddr:
|
||||
return "Delete IP IP"
|
||||
case errClauseInd:
|
||||
return "Error Cause Indication"
|
||||
case setPriAddr:
|
||||
return "Set Primary IP"
|
||||
case successInd:
|
||||
return "Success Indication"
|
||||
case adaptLayerInd:
|
||||
return "Adaptation Layer Indication"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown ParamType: %d", p)
|
||||
}
|
||||
}
|
||||
73
server/vendor/github.com/pion/sctp/payload_queue.go
generated
vendored
Normal file
73
server/vendor/github.com/pion/sctp/payload_queue.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type payloadQueue struct {
|
||||
chunks *queue[*chunkPayloadData]
|
||||
nBytes int
|
||||
}
|
||||
|
||||
func newPayloadQueue() *payloadQueue {
|
||||
return &payloadQueue{chunks: newQueue[*chunkPayloadData](128)}
|
||||
}
|
||||
|
||||
func (q *payloadQueue) pushNoCheck(p *chunkPayloadData) {
|
||||
q.chunks.PushBack(p)
|
||||
q.nBytes += len(p.userData)
|
||||
}
|
||||
|
||||
// pop pops only if the oldest chunk's TSN matches the given TSN.
|
||||
func (q *payloadQueue) pop(tsn uint32) (*chunkPayloadData, bool) {
|
||||
if q.chunks.Len() > 0 && tsn == q.chunks.Front().tsn {
|
||||
c := q.chunks.PopFront()
|
||||
q.nBytes -= len(c.userData)
|
||||
return c, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// get returns reference to chunkPayloadData with the given TSN value.
|
||||
func (q *payloadQueue) get(tsn uint32) (*chunkPayloadData, bool) {
|
||||
length := q.chunks.Len()
|
||||
if length == 0 {
|
||||
return nil, false
|
||||
}
|
||||
head := q.chunks.Front().tsn
|
||||
if tsn < head || int(tsn-head) >= length {
|
||||
return nil, false
|
||||
}
|
||||
return q.chunks.At(int(tsn - head)), true
|
||||
}
|
||||
|
||||
func (q *payloadQueue) markAsAcked(tsn uint32) int {
|
||||
var nBytesAcked int
|
||||
if c, ok := q.get(tsn); ok {
|
||||
c.acked = true
|
||||
c.retransmit = false
|
||||
nBytesAcked = len(c.userData)
|
||||
q.nBytes -= nBytesAcked
|
||||
c.userData = []byte{}
|
||||
}
|
||||
|
||||
return nBytesAcked
|
||||
}
|
||||
|
||||
func (q *payloadQueue) markAllToRetrasmit() {
|
||||
for i := 0; i < q.chunks.Len(); i++ {
|
||||
c := q.chunks.At(i)
|
||||
if c.acked || c.abandoned() {
|
||||
continue
|
||||
}
|
||||
c.retransmit = true
|
||||
}
|
||||
}
|
||||
|
||||
func (q *payloadQueue) getNumBytes() int {
|
||||
return q.nBytes
|
||||
}
|
||||
|
||||
func (q *payloadQueue) size() int {
|
||||
return q.chunks.Len()
|
||||
}
|
||||
142
server/vendor/github.com/pion/sctp/pending_queue.go
generated
vendored
Normal file
142
server/vendor/github.com/pion/sctp/pending_queue.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// pendingBaseQueue
|
||||
|
||||
type pendingBaseQueue struct {
|
||||
queue []*chunkPayloadData
|
||||
}
|
||||
|
||||
func newPendingBaseQueue() *pendingBaseQueue {
|
||||
return &pendingBaseQueue{queue: []*chunkPayloadData{}}
|
||||
}
|
||||
|
||||
func (q *pendingBaseQueue) push(c *chunkPayloadData) {
|
||||
q.queue = append(q.queue, c)
|
||||
}
|
||||
|
||||
func (q *pendingBaseQueue) pop() *chunkPayloadData {
|
||||
if len(q.queue) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := q.queue[0]
|
||||
q.queue = q.queue[1:]
|
||||
return c
|
||||
}
|
||||
|
||||
func (q *pendingBaseQueue) get(i int) *chunkPayloadData {
|
||||
if len(q.queue) == 0 || i < 0 || i >= len(q.queue) {
|
||||
return nil
|
||||
}
|
||||
return q.queue[i]
|
||||
}
|
||||
|
||||
func (q *pendingBaseQueue) size() int {
|
||||
return len(q.queue)
|
||||
}
|
||||
|
||||
// pendingQueue
|
||||
|
||||
type pendingQueue struct {
|
||||
unorderedQueue *pendingBaseQueue
|
||||
orderedQueue *pendingBaseQueue
|
||||
nBytes int
|
||||
selected bool
|
||||
unorderedIsSelected bool
|
||||
}
|
||||
|
||||
// Pending queue errors
|
||||
var (
|
||||
ErrUnexpectedChuckPoppedUnordered = errors.New("unexpected chunk popped (unordered)")
|
||||
ErrUnexpectedChuckPoppedOrdered = errors.New("unexpected chunk popped (ordered)")
|
||||
ErrUnexpectedQState = errors.New("unexpected q state (should've been selected)")
|
||||
)
|
||||
|
||||
func newPendingQueue() *pendingQueue {
|
||||
return &pendingQueue{
|
||||
unorderedQueue: newPendingBaseQueue(),
|
||||
orderedQueue: newPendingBaseQueue(),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *pendingQueue) push(c *chunkPayloadData) {
|
||||
if c.unordered {
|
||||
q.unorderedQueue.push(c)
|
||||
} else {
|
||||
q.orderedQueue.push(c)
|
||||
}
|
||||
q.nBytes += len(c.userData)
|
||||
}
|
||||
|
||||
func (q *pendingQueue) peek() *chunkPayloadData {
|
||||
if q.selected {
|
||||
if q.unorderedIsSelected {
|
||||
return q.unorderedQueue.get(0)
|
||||
}
|
||||
return q.orderedQueue.get(0)
|
||||
}
|
||||
|
||||
if c := q.unorderedQueue.get(0); c != nil {
|
||||
return c
|
||||
}
|
||||
return q.orderedQueue.get(0)
|
||||
}
|
||||
|
||||
func (q *pendingQueue) pop(c *chunkPayloadData) error {
|
||||
if q.selected {
|
||||
var popped *chunkPayloadData
|
||||
if q.unorderedIsSelected {
|
||||
popped = q.unorderedQueue.pop()
|
||||
if popped != c {
|
||||
return ErrUnexpectedChuckPoppedUnordered
|
||||
}
|
||||
} else {
|
||||
popped = q.orderedQueue.pop()
|
||||
if popped != c {
|
||||
return ErrUnexpectedChuckPoppedOrdered
|
||||
}
|
||||
}
|
||||
if popped.endingFragment {
|
||||
q.selected = false
|
||||
}
|
||||
} else {
|
||||
if !c.beginningFragment {
|
||||
return ErrUnexpectedQState
|
||||
}
|
||||
if c.unordered {
|
||||
popped := q.unorderedQueue.pop()
|
||||
if popped != c {
|
||||
return ErrUnexpectedChuckPoppedUnordered
|
||||
}
|
||||
if !popped.endingFragment {
|
||||
q.selected = true
|
||||
q.unorderedIsSelected = true
|
||||
}
|
||||
} else {
|
||||
popped := q.orderedQueue.pop()
|
||||
if popped != c {
|
||||
return ErrUnexpectedChuckPoppedOrdered
|
||||
}
|
||||
if !popped.endingFragment {
|
||||
q.selected = true
|
||||
q.unorderedIsSelected = false
|
||||
}
|
||||
}
|
||||
}
|
||||
q.nBytes -= len(c.userData)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *pendingQueue) getNumBytes() int {
|
||||
return q.nBytes
|
||||
}
|
||||
|
||||
func (q *pendingQueue) size() int {
|
||||
return q.unorderedQueue.size() + q.orderedQueue.size()
|
||||
}
|
||||
70
server/vendor/github.com/pion/sctp/queue.go
generated
vendored
Normal file
70
server/vendor/github.com/pion/sctp/queue.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
type queue[T any] struct {
|
||||
buf []T
|
||||
head int
|
||||
tail int
|
||||
count int
|
||||
}
|
||||
|
||||
const minCap = 16
|
||||
|
||||
func newQueue[T any](capacity int) *queue[T] {
|
||||
queueCap := minCap
|
||||
for queueCap < capacity {
|
||||
queueCap <<= 1
|
||||
}
|
||||
|
||||
return &queue[T]{
|
||||
buf: make([]T, queueCap),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *queue[T]) Len() int {
|
||||
return q.count
|
||||
}
|
||||
|
||||
func (q *queue[T]) PushBack(ele T) {
|
||||
q.growIfFull()
|
||||
q.buf[q.tail] = ele
|
||||
q.tail = (q.tail + 1) % len(q.buf)
|
||||
q.count++
|
||||
}
|
||||
|
||||
func (q *queue[T]) PopFront() T {
|
||||
ele := q.buf[q.head]
|
||||
var zeroVal T
|
||||
q.buf[q.head] = zeroVal
|
||||
q.head = (q.head + 1) % len(q.buf)
|
||||
q.count--
|
||||
return ele
|
||||
}
|
||||
|
||||
func (q *queue[T]) Front() T {
|
||||
return q.buf[q.head]
|
||||
}
|
||||
|
||||
func (q *queue[T]) At(i int) T {
|
||||
return q.buf[(q.head+i)%(len(q.buf))]
|
||||
}
|
||||
|
||||
func (q *queue[T]) growIfFull() {
|
||||
if q.count < len(q.buf) {
|
||||
return
|
||||
}
|
||||
|
||||
newBuf := make([]T, q.count<<1)
|
||||
if q.tail > q.head {
|
||||
copy(newBuf, q.buf[q.head:q.tail])
|
||||
} else {
|
||||
n := copy(newBuf, q.buf[q.head:])
|
||||
copy(newBuf[n:], q.buf[:q.tail])
|
||||
}
|
||||
|
||||
q.head = 0
|
||||
q.tail = q.count
|
||||
q.buf = newBuf
|
||||
}
|
||||
366
server/vendor/github.com/pion/sctp/reassembly_queue.go
generated
vendored
Normal file
366
server/vendor/github.com/pion/sctp/reassembly_queue.go
generated
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func sortChunksByTSN(a []*chunkPayloadData) {
|
||||
sort.Slice(a, func(i, j int) bool {
|
||||
return sna32LT(a[i].tsn, a[j].tsn)
|
||||
})
|
||||
}
|
||||
|
||||
func sortChunksBySSN(a []*chunkSet) {
|
||||
sort.Slice(a, func(i, j int) bool {
|
||||
return sna16LT(a[i].ssn, a[j].ssn)
|
||||
})
|
||||
}
|
||||
|
||||
// chunkSet is a set of chunks that share the same SSN
|
||||
type chunkSet struct {
|
||||
ssn uint16 // used only with the ordered chunks
|
||||
ppi PayloadProtocolIdentifier
|
||||
chunks []*chunkPayloadData
|
||||
}
|
||||
|
||||
func newChunkSet(ssn uint16, ppi PayloadProtocolIdentifier) *chunkSet {
|
||||
return &chunkSet{
|
||||
ssn: ssn,
|
||||
ppi: ppi,
|
||||
chunks: []*chunkPayloadData{},
|
||||
}
|
||||
}
|
||||
|
||||
func (set *chunkSet) push(chunk *chunkPayloadData) bool {
|
||||
// check if dup
|
||||
for _, c := range set.chunks {
|
||||
if c.tsn == chunk.tsn {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// append and sort
|
||||
set.chunks = append(set.chunks, chunk)
|
||||
sortChunksByTSN(set.chunks)
|
||||
|
||||
// Check if we now have a complete set
|
||||
complete := set.isComplete()
|
||||
return complete
|
||||
}
|
||||
|
||||
func (set *chunkSet) isComplete() bool {
|
||||
// Condition for complete set
|
||||
// 0. Has at least one chunk.
|
||||
// 1. Begins with beginningFragment set to true
|
||||
// 2. Ends with endingFragment set to true
|
||||
// 3. TSN monotinically increase by 1 from beginning to end
|
||||
|
||||
// 0.
|
||||
nChunks := len(set.chunks)
|
||||
if nChunks == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 1.
|
||||
if !set.chunks[0].beginningFragment {
|
||||
return false
|
||||
}
|
||||
|
||||
// 2.
|
||||
if !set.chunks[nChunks-1].endingFragment {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3.
|
||||
var lastTSN uint32
|
||||
for i, c := range set.chunks {
|
||||
if i > 0 {
|
||||
// Fragments must have contiguous TSN
|
||||
// From RFC 4960 Section 3.3.1:
|
||||
// When a user message is fragmented into multiple chunks, the TSNs are
|
||||
// used by the receiver to reassemble the message. This means that the
|
||||
// TSNs for each fragment of a fragmented user message MUST be strictly
|
||||
// sequential.
|
||||
if c.tsn != lastTSN+1 {
|
||||
// mid or end fragment is missing
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
lastTSN = c.tsn
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type reassemblyQueue struct {
|
||||
si uint16
|
||||
nextSSN uint16 // expected SSN for next ordered chunk
|
||||
ordered []*chunkSet
|
||||
unordered []*chunkSet
|
||||
unorderedChunks []*chunkPayloadData
|
||||
nBytes uint64
|
||||
}
|
||||
|
||||
var errTryAgain = errors.New("try again")
|
||||
|
||||
func newReassemblyQueue(si uint16) *reassemblyQueue {
|
||||
// From RFC 4960 Sec 6.5:
|
||||
// The Stream Sequence Number in all the streams MUST start from 0 when
|
||||
// the association is established. Also, when the Stream Sequence
|
||||
// Number reaches the value 65535 the next Stream Sequence Number MUST
|
||||
// be set to 0.
|
||||
return &reassemblyQueue{
|
||||
si: si,
|
||||
nextSSN: 0, // From RFC 4960 Sec 6.5:
|
||||
ordered: make([]*chunkSet, 0),
|
||||
unordered: make([]*chunkSet, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) push(chunk *chunkPayloadData) bool {
|
||||
var cset *chunkSet
|
||||
|
||||
if chunk.streamIdentifier != r.si {
|
||||
return false
|
||||
}
|
||||
|
||||
if chunk.unordered {
|
||||
// First, insert into unorderedChunks array
|
||||
r.unorderedChunks = append(r.unorderedChunks, chunk)
|
||||
atomic.AddUint64(&r.nBytes, uint64(len(chunk.userData)))
|
||||
sortChunksByTSN(r.unorderedChunks)
|
||||
|
||||
// Scan unorderedChunks that are contiguous (in TSN)
|
||||
cset = r.findCompleteUnorderedChunkSet()
|
||||
|
||||
// If found, append the complete set to the unordered array
|
||||
if cset != nil {
|
||||
r.unordered = append(r.unordered, cset)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// This is an ordered chunk
|
||||
|
||||
if sna16LT(chunk.streamSequenceNumber, r.nextSSN) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if a fragmented chunkSet with the fragmented SSN already exists
|
||||
if chunk.isFragmented() {
|
||||
for _, set := range r.ordered {
|
||||
// nolint:godox
|
||||
// TODO: add caution around SSN wrapping here... this helps only a little bit
|
||||
// by ensuring we don't add to an unfragmented cset (1 chunk). There's
|
||||
// a case where if the SSN does wrap around, we may see the same SSN
|
||||
// for a different chunk.
|
||||
|
||||
// nolint:godox
|
||||
// TODO: this slice can get pretty big; it may be worth maintaining a map
|
||||
// for O(1) lookups at the cost of 2x memory.
|
||||
if set.ssn == chunk.streamSequenceNumber && set.chunks[0].isFragmented() {
|
||||
cset = set
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, create a new chunkSet
|
||||
if cset == nil {
|
||||
cset = newChunkSet(chunk.streamSequenceNumber, chunk.payloadType)
|
||||
r.ordered = append(r.ordered, cset)
|
||||
if !chunk.unordered {
|
||||
sortChunksBySSN(r.ordered)
|
||||
}
|
||||
}
|
||||
|
||||
atomic.AddUint64(&r.nBytes, uint64(len(chunk.userData)))
|
||||
|
||||
return cset.push(chunk)
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) findCompleteUnorderedChunkSet() *chunkSet {
|
||||
startIdx := -1
|
||||
nChunks := 0
|
||||
var lastTSN uint32
|
||||
var found bool
|
||||
|
||||
for i, c := range r.unorderedChunks {
|
||||
// seek beigining
|
||||
if c.beginningFragment {
|
||||
startIdx = i
|
||||
nChunks = 1
|
||||
lastTSN = c.tsn
|
||||
|
||||
if c.endingFragment {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if startIdx < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if contiguous in TSN
|
||||
if c.tsn != lastTSN+1 {
|
||||
startIdx = -1
|
||||
continue
|
||||
}
|
||||
|
||||
lastTSN = c.tsn
|
||||
nChunks++
|
||||
|
||||
if c.endingFragment {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract the range of chunks
|
||||
var chunks []*chunkPayloadData
|
||||
chunks = append(chunks, r.unorderedChunks[startIdx:startIdx+nChunks]...)
|
||||
|
||||
r.unorderedChunks = append(
|
||||
r.unorderedChunks[:startIdx],
|
||||
r.unorderedChunks[startIdx+nChunks:]...)
|
||||
|
||||
chunkSet := newChunkSet(0, chunks[0].payloadType)
|
||||
chunkSet.chunks = chunks
|
||||
|
||||
return chunkSet
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) isReadable() bool {
|
||||
// Check unordered first
|
||||
if len(r.unordered) > 0 {
|
||||
// The chunk sets in r.unordered should all be complete.
|
||||
return true
|
||||
}
|
||||
|
||||
// Check ordered sets
|
||||
if len(r.ordered) > 0 {
|
||||
cset := r.ordered[0]
|
||||
if cset.isComplete() {
|
||||
if sna16LTE(cset.ssn, r.nextSSN) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) read(buf []byte) (int, PayloadProtocolIdentifier, error) {
|
||||
var cset *chunkSet
|
||||
// Check unordered first
|
||||
switch {
|
||||
case len(r.unordered) > 0:
|
||||
cset = r.unordered[0]
|
||||
r.unordered = r.unordered[1:]
|
||||
case len(r.ordered) > 0:
|
||||
// Now, check ordered
|
||||
cset = r.ordered[0]
|
||||
if !cset.isComplete() {
|
||||
return 0, 0, errTryAgain
|
||||
}
|
||||
if sna16GT(cset.ssn, r.nextSSN) {
|
||||
return 0, 0, errTryAgain
|
||||
}
|
||||
r.ordered = r.ordered[1:]
|
||||
if cset.ssn == r.nextSSN {
|
||||
r.nextSSN++
|
||||
}
|
||||
default:
|
||||
return 0, 0, errTryAgain
|
||||
}
|
||||
|
||||
// Concat all fragments into the buffer
|
||||
nWritten := 0
|
||||
ppi := cset.ppi
|
||||
var err error
|
||||
for _, c := range cset.chunks {
|
||||
toCopy := len(c.userData)
|
||||
r.subtractNumBytes(toCopy)
|
||||
if err == nil {
|
||||
n := copy(buf[nWritten:], c.userData)
|
||||
nWritten += n
|
||||
if n < toCopy {
|
||||
err = io.ErrShortBuffer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nWritten, ppi, err
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) forwardTSNForOrdered(lastSSN uint16) {
|
||||
// Use lastSSN to locate a chunkSet then remove it if the set has
|
||||
// not been complete
|
||||
keep := []*chunkSet{}
|
||||
for _, set := range r.ordered {
|
||||
if sna16LTE(set.ssn, lastSSN) {
|
||||
if !set.isComplete() {
|
||||
// drop the set
|
||||
for _, c := range set.chunks {
|
||||
r.subtractNumBytes(len(c.userData))
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
keep = append(keep, set)
|
||||
}
|
||||
r.ordered = keep
|
||||
|
||||
// Finally, forward nextSSN
|
||||
if sna16LTE(r.nextSSN, lastSSN) {
|
||||
r.nextSSN = lastSSN + 1
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) forwardTSNForUnordered(newCumulativeTSN uint32) {
|
||||
// Remove all fragments in the unordered sets that contains chunks
|
||||
// equal to or older than `newCumulativeTSN`.
|
||||
// We know all sets in the r.unordered are complete ones.
|
||||
// Just remove chunks that are equal to or older than newCumulativeTSN
|
||||
// from the unorderedChunks
|
||||
lastIdx := -1
|
||||
for i, c := range r.unorderedChunks {
|
||||
if sna32GT(c.tsn, newCumulativeTSN) {
|
||||
break
|
||||
}
|
||||
lastIdx = i
|
||||
}
|
||||
if lastIdx >= 0 {
|
||||
for _, c := range r.unorderedChunks[0 : lastIdx+1] {
|
||||
r.subtractNumBytes(len(c.userData))
|
||||
}
|
||||
r.unorderedChunks = r.unorderedChunks[lastIdx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) subtractNumBytes(nBytes int) {
|
||||
cur := atomic.LoadUint64(&r.nBytes)
|
||||
if int(cur) >= nBytes {
|
||||
atomic.AddUint64(&r.nBytes, -uint64(nBytes))
|
||||
} else {
|
||||
atomic.StoreUint64(&r.nBytes, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reassemblyQueue) getNumBytes() int {
|
||||
return int(atomic.LoadUint64(&r.nBytes))
|
||||
}
|
||||
186
server/vendor/github.com/pion/sctp/receive_payload_queue.go
generated
vendored
Normal file
186
server/vendor/github.com/pion/sctp/receive_payload_queue.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type receivePayloadQueue struct {
|
||||
tailTSN uint32
|
||||
chunkSize int
|
||||
tsnBitmask []uint64
|
||||
dupTSN []uint32
|
||||
maxTSNOffset uint32
|
||||
|
||||
cumulativeTSN uint32
|
||||
}
|
||||
|
||||
func newReceivePayloadQueue(maxTSNOffset uint32) *receivePayloadQueue {
|
||||
maxTSNOffset = ((maxTSNOffset + 63) / 64) * 64
|
||||
return &receivePayloadQueue{
|
||||
tsnBitmask: make([]uint64, maxTSNOffset/64),
|
||||
maxTSNOffset: maxTSNOffset,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) init(cumulativeTSN uint32) {
|
||||
q.cumulativeTSN = cumulativeTSN
|
||||
q.tailTSN = cumulativeTSN
|
||||
q.chunkSize = 0
|
||||
for i := range q.tsnBitmask {
|
||||
q.tsnBitmask[i] = 0
|
||||
}
|
||||
q.dupTSN = q.dupTSN[:0]
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) hasChunk(tsn uint32) bool {
|
||||
if q.chunkSize == 0 || sna32LTE(tsn, q.cumulativeTSN) || sna32GT(tsn, q.tailTSN) {
|
||||
return false
|
||||
}
|
||||
|
||||
index, offset := int(tsn/64)%len(q.tsnBitmask), tsn%64
|
||||
return q.tsnBitmask[index]&(1<<offset) != 0
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) canPush(tsn uint32) bool {
|
||||
ok := q.hasChunk(tsn)
|
||||
if ok || sna32LTE(tsn, q.cumulativeTSN) || sna32GT(tsn, q.cumulativeTSN+q.maxTSNOffset) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// push pushes a payload data. If the payload data is already in our queue or
|
||||
// older than our cumulativeTSN marker, it will be recored as duplications,
|
||||
// which can later be retrieved using popDuplicates.
|
||||
func (q *receivePayloadQueue) push(tsn uint32) bool {
|
||||
if sna32GT(tsn, q.cumulativeTSN+q.maxTSNOffset) {
|
||||
return false
|
||||
}
|
||||
|
||||
if sna32LTE(tsn, q.cumulativeTSN) || q.hasChunk(tsn) {
|
||||
// Found the packet, log in dups
|
||||
q.dupTSN = append(q.dupTSN, tsn)
|
||||
return false
|
||||
}
|
||||
|
||||
index, offset := int(tsn/64)%len(q.tsnBitmask), tsn%64
|
||||
q.tsnBitmask[index] |= (1 << offset)
|
||||
q.chunkSize++
|
||||
if sna32GT(tsn, q.tailTSN) {
|
||||
q.tailTSN = tsn
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// pop advances cumulativeTSN and pops the oldest chunk's TSN if it matches the given TSN or force is true.
|
||||
func (q *receivePayloadQueue) pop(force bool) bool {
|
||||
tsn := q.cumulativeTSN + 1
|
||||
if q.hasChunk(tsn) {
|
||||
index, offset := int(tsn/64)%len(q.tsnBitmask), int(tsn%64)
|
||||
q.tsnBitmask[index] &= ^uint64(1 << (offset))
|
||||
q.chunkSize--
|
||||
q.cumulativeTSN++
|
||||
return true
|
||||
}
|
||||
if force {
|
||||
q.cumulativeTSN++
|
||||
if q.chunkSize == 0 {
|
||||
q.tailTSN = q.cumulativeTSN
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// popDuplicates returns an array of TSN values that were found duplicate.
|
||||
func (q *receivePayloadQueue) popDuplicates() []uint32 {
|
||||
dups := q.dupTSN
|
||||
q.dupTSN = []uint32{}
|
||||
return dups
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) getGapAckBlocks() (gapAckBlocks []gapAckBlock) {
|
||||
var b gapAckBlock
|
||||
|
||||
if q.chunkSize == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
startTSN, endTSN := q.cumulativeTSN+1, q.tailTSN
|
||||
var findEnd bool
|
||||
for tsn := startTSN; sna32LTE(tsn, endTSN); {
|
||||
index, offset := int(tsn/64)%len(q.tsnBitmask), int(tsn%64)
|
||||
if !findEnd {
|
||||
// find first received tsn as start
|
||||
if nonZeroBit, ok := getFirstNonZeroBit(q.tsnBitmask[index], offset, 64); ok {
|
||||
b.start = uint16(tsn + uint32(nonZeroBit-offset) - q.cumulativeTSN)
|
||||
tsn += uint32(nonZeroBit - offset)
|
||||
findEnd = true
|
||||
} else {
|
||||
// no result, find start bits in next uint64 bitmask
|
||||
tsn += uint32(64 - offset)
|
||||
}
|
||||
} else {
|
||||
if zeroBit, ok := getFirstZeroBit(q.tsnBitmask[index], offset, 64); ok {
|
||||
b.end = uint16(tsn + uint32(zeroBit-offset) - 1 - q.cumulativeTSN)
|
||||
tsn += uint32(zeroBit - offset)
|
||||
if sna32LTE(tsn, endTSN) {
|
||||
gapAckBlocks = append(gapAckBlocks, gapAckBlock{
|
||||
start: b.start,
|
||||
end: b.end,
|
||||
})
|
||||
}
|
||||
findEnd = false
|
||||
} else {
|
||||
tsn += uint32(64 - offset)
|
||||
}
|
||||
|
||||
// no zero bit at the end, close and append the last gap
|
||||
if sna32GT(tsn, endTSN) {
|
||||
b.end = uint16(endTSN - q.cumulativeTSN)
|
||||
gapAckBlocks = append(gapAckBlocks, gapAckBlock{
|
||||
start: b.start,
|
||||
end: b.end,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return gapAckBlocks
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) getGapAckBlocksString() string {
|
||||
gapAckBlocks := q.getGapAckBlocks()
|
||||
str := fmt.Sprintf("cumTSN=%d", q.cumulativeTSN)
|
||||
for _, b := range gapAckBlocks {
|
||||
str += fmt.Sprintf(",%d-%d", b.start, b.end)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) getLastTSNReceived() (uint32, bool) {
|
||||
if q.chunkSize == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return q.tailTSN, true
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) getcumulativeTSN() uint32 {
|
||||
return q.cumulativeTSN
|
||||
}
|
||||
|
||||
func (q *receivePayloadQueue) size() int {
|
||||
return q.chunkSize
|
||||
}
|
||||
|
||||
func getFirstNonZeroBit(val uint64, start, end int) (int, bool) {
|
||||
i := bits.TrailingZeros64(val >> uint64(start))
|
||||
return i + start, i+start < end
|
||||
}
|
||||
|
||||
func getFirstZeroBit(val uint64, start, end int) (int, bool) {
|
||||
return getFirstNonZeroBit(^val, start, end)
|
||||
}
|
||||
6
server/vendor/github.com/pion/sctp/renovate.json
generated
vendored
Normal file
6
server/vendor/github.com/pion/sctp/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>pion/renovate-config"
|
||||
]
|
||||
}
|
||||
250
server/vendor/github.com/pion/sctp/rtx_timer.go
generated
vendored
Normal file
250
server/vendor/github.com/pion/sctp/rtx_timer.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// RTO.Initial in msec
|
||||
rtoInitial float64 = 1.0 * 1000
|
||||
|
||||
// RTO.Min in msec
|
||||
rtoMin float64 = 1.0 * 1000
|
||||
|
||||
// RTO.Max in msec
|
||||
defaultRTOMax float64 = 60.0 * 1000
|
||||
|
||||
// RTO.Alpha
|
||||
rtoAlpha float64 = 0.125
|
||||
|
||||
// RTO.Beta
|
||||
rtoBeta float64 = 0.25
|
||||
|
||||
// Max.Init.Retransmits:
|
||||
maxInitRetrans uint = 8
|
||||
|
||||
// Path.Max.Retrans
|
||||
pathMaxRetrans uint = 5
|
||||
|
||||
noMaxRetrans uint = 0
|
||||
)
|
||||
|
||||
// rtoManager manages Rtx timeout values.
|
||||
// This is an implementation of RFC 4960 sec 6.3.1.
|
||||
type rtoManager struct {
|
||||
srtt float64
|
||||
rttvar float64
|
||||
rto float64
|
||||
noUpdate bool
|
||||
mutex sync.RWMutex
|
||||
rtoMax float64
|
||||
}
|
||||
|
||||
// newRTOManager creates a new rtoManager.
|
||||
func newRTOManager(rtoMax float64) *rtoManager {
|
||||
mgr := rtoManager{
|
||||
rto: rtoInitial,
|
||||
rtoMax: rtoMax,
|
||||
}
|
||||
if mgr.rtoMax == 0 {
|
||||
mgr.rtoMax = defaultRTOMax
|
||||
}
|
||||
return &mgr
|
||||
}
|
||||
|
||||
// setNewRTT takes a newly measured RTT then adjust the RTO in msec.
|
||||
func (m *rtoManager) setNewRTT(rtt float64) float64 {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if m.noUpdate {
|
||||
return m.srtt
|
||||
}
|
||||
|
||||
if m.srtt == 0 {
|
||||
// First measurement
|
||||
m.srtt = rtt
|
||||
m.rttvar = rtt / 2
|
||||
} else {
|
||||
// Subsequent rtt measurement
|
||||
m.rttvar = (1-rtoBeta)*m.rttvar + rtoBeta*(math.Abs(m.srtt-rtt))
|
||||
m.srtt = (1-rtoAlpha)*m.srtt + rtoAlpha*rtt
|
||||
}
|
||||
m.rto = math.Min(math.Max(m.srtt+4*m.rttvar, rtoMin), m.rtoMax)
|
||||
return m.srtt
|
||||
}
|
||||
|
||||
// getRTO simply returns the current RTO in msec.
|
||||
func (m *rtoManager) getRTO() float64 {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
return m.rto
|
||||
}
|
||||
|
||||
// reset resets the RTO variables to the initial values.
|
||||
func (m *rtoManager) reset() {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
if m.noUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
m.srtt = 0
|
||||
m.rttvar = 0
|
||||
m.rto = rtoInitial
|
||||
}
|
||||
|
||||
// set RTO value for testing
|
||||
func (m *rtoManager) setRTO(rto float64, noUpdate bool) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
m.rto = rto
|
||||
m.noUpdate = noUpdate
|
||||
}
|
||||
|
||||
// rtxTimerObserver is the inteface to a timer observer.
|
||||
// NOTE: Observers MUST NOT call start() or stop() method on rtxTimer
|
||||
// from within these callbacks.
|
||||
type rtxTimerObserver interface {
|
||||
onRetransmissionTimeout(timerID int, n uint)
|
||||
onRetransmissionFailure(timerID int)
|
||||
}
|
||||
|
||||
type rtxTimerState uint8
|
||||
|
||||
const (
|
||||
rtxTimerStopped rtxTimerState = iota
|
||||
rtxTimerStarted
|
||||
rtxTimerClosed
|
||||
)
|
||||
|
||||
// rtxTimer provides the retnransmission timer conforms with RFC 4960 Sec 6.3.1
|
||||
type rtxTimer struct {
|
||||
timer *time.Timer
|
||||
observer rtxTimerObserver
|
||||
id int
|
||||
maxRetrans uint
|
||||
rtoMax float64
|
||||
mutex sync.Mutex
|
||||
rto float64
|
||||
nRtos uint
|
||||
state rtxTimerState
|
||||
pending uint8
|
||||
}
|
||||
|
||||
// newRTXTimer creates a new retransmission timer.
|
||||
// if maxRetrans is set to 0, it will keep retransmitting until stop() is called.
|
||||
// (it will never make onRetransmissionFailure() callback.
|
||||
func newRTXTimer(id int, observer rtxTimerObserver, maxRetrans uint,
|
||||
rtoMax float64,
|
||||
) *rtxTimer {
|
||||
timer := rtxTimer{
|
||||
id: id,
|
||||
observer: observer,
|
||||
maxRetrans: maxRetrans,
|
||||
rtoMax: rtoMax,
|
||||
}
|
||||
if timer.rtoMax == 0 {
|
||||
timer.rtoMax = defaultRTOMax
|
||||
}
|
||||
timer.timer = time.AfterFunc(math.MaxInt64, timer.timeout)
|
||||
timer.timer.Stop()
|
||||
return &timer
|
||||
}
|
||||
|
||||
func (t *rtxTimer) calculateNextTimeout() time.Duration {
|
||||
timeout := calculateNextTimeout(t.rto, t.nRtos, t.rtoMax)
|
||||
return time.Duration(timeout) * time.Millisecond
|
||||
}
|
||||
|
||||
func (t *rtxTimer) timeout() {
|
||||
t.mutex.Lock()
|
||||
if t.pending--; t.pending == 0 && t.state == rtxTimerStarted {
|
||||
if t.nRtos++; t.maxRetrans == 0 || t.nRtos <= t.maxRetrans {
|
||||
t.timer.Reset(t.calculateNextTimeout())
|
||||
t.pending++
|
||||
defer t.observer.onRetransmissionTimeout(t.id, t.nRtos)
|
||||
} else {
|
||||
t.state = rtxTimerStopped
|
||||
defer t.observer.onRetransmissionFailure(t.id)
|
||||
}
|
||||
}
|
||||
t.mutex.Unlock()
|
||||
}
|
||||
|
||||
// start starts the timer.
|
||||
func (t *rtxTimer) start(rto float64) bool {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
// this timer is already closed or aleady running
|
||||
if t.state != rtxTimerStopped {
|
||||
return false
|
||||
}
|
||||
|
||||
// Note: rto value is intentionally not capped by RTO.Min to allow
|
||||
// fast timeout for the tests. Non-test code should pass in the
|
||||
// rto generated by rtoManager getRTO() method which caps the
|
||||
// value at RTO.Min or at RTO.Max.
|
||||
t.rto = rto
|
||||
t.nRtos = 0
|
||||
t.state = rtxTimerStarted
|
||||
t.pending++
|
||||
t.timer.Reset(t.calculateNextTimeout())
|
||||
return true
|
||||
}
|
||||
|
||||
// stop stops the timer.
|
||||
func (t *rtxTimer) stop() {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
if t.state == rtxTimerStarted {
|
||||
if t.timer.Stop() {
|
||||
t.pending--
|
||||
}
|
||||
t.state = rtxTimerStopped
|
||||
}
|
||||
}
|
||||
|
||||
// closes the timer. this is similar to stop() but subsequent start() call
|
||||
// will fail (the timer is no longer usable)
|
||||
func (t *rtxTimer) close() {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
if t.state == rtxTimerStarted && t.timer.Stop() {
|
||||
t.pending--
|
||||
}
|
||||
t.state = rtxTimerClosed
|
||||
}
|
||||
|
||||
// isRunning tests if the timer is running.
|
||||
// Debug purpose only
|
||||
func (t *rtxTimer) isRunning() bool {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
return t.state == rtxTimerStarted
|
||||
}
|
||||
|
||||
func calculateNextTimeout(rto float64, nRtos uint, rtoMax float64) float64 {
|
||||
// RFC 4096 sec 6.3.3. Handle T3-rtx Expiration
|
||||
// E2) For the destination address for which the timer expires, set RTO
|
||||
// <- RTO * 2 ("back off the timer"). The maximum value discussed
|
||||
// in rule C7 above (RTO.max) may be used to provide an upper bound
|
||||
// to this doubling operation.
|
||||
if nRtos < 31 {
|
||||
m := 1 << nRtos
|
||||
return math.Min(rto*float64(m), rtoMax)
|
||||
}
|
||||
return rtoMax
|
||||
}
|
||||
5
server/vendor/github.com/pion/sctp/sctp.go
generated
vendored
Normal file
5
server/vendor/github.com/pion/sctp/sctp.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package sctp implements the SCTP spec
|
||||
package sctp
|
||||
471
server/vendor/github.com/pion/sctp/stream.go
generated
vendored
Normal file
471
server/vendor/github.com/pion/sctp/stream.go
generated
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReliabilityTypeReliable is used for reliable transmission
|
||||
ReliabilityTypeReliable byte = 0
|
||||
// ReliabilityTypeRexmit is used for partial reliability by retransmission count
|
||||
ReliabilityTypeRexmit byte = 1
|
||||
// ReliabilityTypeTimed is used for partial reliability by retransmission duration
|
||||
ReliabilityTypeTimed byte = 2
|
||||
)
|
||||
|
||||
// StreamState is an enum for SCTP Stream state field
|
||||
// This field identifies the state of stream.
|
||||
type StreamState int
|
||||
|
||||
// StreamState enums
|
||||
const (
|
||||
StreamStateOpen StreamState = iota // Stream object starts with StreamStateOpen
|
||||
StreamStateClosing // Outgoing stream is being reset
|
||||
StreamStateClosed // Stream has been closed
|
||||
)
|
||||
|
||||
func (ss StreamState) String() string {
|
||||
switch ss {
|
||||
case StreamStateOpen:
|
||||
return "open"
|
||||
case StreamStateClosing:
|
||||
return "closing"
|
||||
case StreamStateClosed:
|
||||
return "closed"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// SCTP stream errors
|
||||
var (
|
||||
ErrOutboundPacketTooLarge = errors.New("outbound packet larger than maximum message size")
|
||||
ErrStreamClosed = errors.New("stream closed")
|
||||
ErrReadDeadlineExceeded = fmt.Errorf("read deadline exceeded: %w", os.ErrDeadlineExceeded)
|
||||
)
|
||||
|
||||
// Stream represents an SCTP stream
|
||||
type Stream struct {
|
||||
association *Association
|
||||
lock sync.RWMutex
|
||||
streamIdentifier uint16
|
||||
defaultPayloadType PayloadProtocolIdentifier
|
||||
reassemblyQueue *reassemblyQueue
|
||||
sequenceNumber uint16
|
||||
readNotifier *sync.Cond
|
||||
readErr error
|
||||
readTimeoutCancel chan struct{}
|
||||
unordered bool
|
||||
reliabilityType byte
|
||||
reliabilityValue uint32
|
||||
bufferedAmount uint64
|
||||
bufferedAmountLow uint64
|
||||
onBufferedAmountLow func()
|
||||
state StreamState
|
||||
log logging.LeveledLogger
|
||||
name string
|
||||
}
|
||||
|
||||
// StreamIdentifier returns the Stream identifier associated to the stream.
|
||||
func (s *Stream) StreamIdentifier() uint16 {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.streamIdentifier
|
||||
}
|
||||
|
||||
// SetDefaultPayloadType sets the default payload type used by Write.
|
||||
func (s *Stream) SetDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
|
||||
atomic.StoreUint32((*uint32)(&s.defaultPayloadType), uint32(defaultPayloadType))
|
||||
}
|
||||
|
||||
// SetReliabilityParams sets reliability parameters for this stream.
|
||||
func (s *Stream) SetReliabilityParams(unordered bool, relType byte, relVal uint32) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.setReliabilityParams(unordered, relType, relVal)
|
||||
}
|
||||
|
||||
// setReliabilityParams sets reliability parameters for this stream.
|
||||
// The caller should hold the lock.
|
||||
func (s *Stream) setReliabilityParams(unordered bool, relType byte, relVal uint32) {
|
||||
s.log.Debugf("[%s] reliability params: ordered=%v type=%d value=%d",
|
||||
s.name, !unordered, relType, relVal)
|
||||
s.unordered = unordered
|
||||
s.reliabilityType = relType
|
||||
s.reliabilityValue = relVal
|
||||
}
|
||||
|
||||
// Read reads a packet of len(p) bytes, dropping the Payload Protocol Identifier.
|
||||
// Returns EOF when the stream is reset or an error if the stream is closed
|
||||
// otherwise.
|
||||
func (s *Stream) Read(p []byte) (int, error) {
|
||||
n, _, err := s.ReadSCTP(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadSCTP reads a packet of len(p) bytes and returns the associated Payload
|
||||
// Protocol Identifier.
|
||||
// Returns EOF when the stream is reset or an error if the stream is closed
|
||||
// otherwise.
|
||||
func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
defer func() {
|
||||
// close readTimeoutCancel if the current read timeout routine is no longer effective
|
||||
if s.readTimeoutCancel != nil && s.readErr != nil {
|
||||
close(s.readTimeoutCancel)
|
||||
s.readTimeoutCancel = nil
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
n, ppi, err := s.reassemblyQueue.read(p)
|
||||
if err == nil {
|
||||
return n, ppi, nil
|
||||
} else if errors.Is(err, io.ErrShortBuffer) {
|
||||
return 0, PayloadProtocolIdentifier(0), err
|
||||
}
|
||||
|
||||
err = s.readErr
|
||||
if err != nil {
|
||||
return 0, PayloadProtocolIdentifier(0), err
|
||||
}
|
||||
|
||||
s.readNotifier.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline in an identical way to net.Conn
|
||||
func (s *Stream) SetReadDeadline(deadline time.Time) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.readTimeoutCancel != nil {
|
||||
close(s.readTimeoutCancel)
|
||||
s.readTimeoutCancel = nil
|
||||
}
|
||||
|
||||
if s.readErr != nil {
|
||||
if !errors.Is(s.readErr, ErrReadDeadlineExceeded) {
|
||||
return nil
|
||||
}
|
||||
s.readErr = nil
|
||||
}
|
||||
|
||||
if !deadline.IsZero() {
|
||||
s.readTimeoutCancel = make(chan struct{})
|
||||
|
||||
go func(readTimeoutCancel chan struct{}) {
|
||||
t := time.NewTimer(time.Until(deadline))
|
||||
select {
|
||||
case <-readTimeoutCancel:
|
||||
t.Stop()
|
||||
return
|
||||
case <-t.C:
|
||||
select {
|
||||
case <-readTimeoutCancel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
s.lock.Lock()
|
||||
if s.readErr == nil {
|
||||
s.readErr = ErrReadDeadlineExceeded
|
||||
}
|
||||
s.readTimeoutCancel = nil
|
||||
s.lock.Unlock()
|
||||
|
||||
s.readNotifier.Signal()
|
||||
}
|
||||
}(s.readTimeoutCancel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stream) handleData(pd *chunkPayloadData) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
var readable bool
|
||||
if s.reassemblyQueue.push(pd) {
|
||||
readable = s.reassemblyQueue.isReadable()
|
||||
s.log.Debugf("[%s] reassemblyQueue readable=%v", s.name, readable)
|
||||
if readable {
|
||||
s.log.Debugf("[%s] readNotifier.signal()", s.name)
|
||||
s.readNotifier.Signal()
|
||||
s.log.Debugf("[%s] readNotifier.signal() done", s.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) handleForwardTSNForOrdered(ssn uint16) {
|
||||
var readable bool
|
||||
|
||||
func() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if s.unordered {
|
||||
return // unordered chunks are handled by handleForwardUnordered method
|
||||
}
|
||||
|
||||
// Remove all chunks older than or equal to the new TSN from
|
||||
// the reassemblyQueue.
|
||||
s.reassemblyQueue.forwardTSNForOrdered(ssn)
|
||||
readable = s.reassemblyQueue.isReadable()
|
||||
}()
|
||||
|
||||
// Notify the reader asynchronously if there's a data chunk to read.
|
||||
if readable {
|
||||
s.readNotifier.Signal()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) handleForwardTSNForUnordered(newCumulativeTSN uint32) {
|
||||
var readable bool
|
||||
|
||||
func() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if !s.unordered {
|
||||
return // ordered chunks are handled by handleForwardTSNOrdered method
|
||||
}
|
||||
|
||||
// Remove all chunks older than or equal to the new TSN from
|
||||
// the reassemblyQueue.
|
||||
s.reassemblyQueue.forwardTSNForUnordered(newCumulativeTSN)
|
||||
readable = s.reassemblyQueue.isReadable()
|
||||
}()
|
||||
|
||||
// Notify the reader asynchronously if there's a data chunk to read.
|
||||
if readable {
|
||||
s.readNotifier.Signal()
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p with the default Payload Protocol Identifier
|
||||
func (s *Stream) Write(p []byte) (n int, err error) {
|
||||
ppi := PayloadProtocolIdentifier(atomic.LoadUint32((*uint32)(&s.defaultPayloadType)))
|
||||
return s.WriteSCTP(p, ppi)
|
||||
}
|
||||
|
||||
// WriteSCTP writes len(p) bytes from p to the DTLS connection
|
||||
func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (int, error) {
|
||||
maxMessageSize := s.association.MaxMessageSize()
|
||||
if len(p) > int(maxMessageSize) {
|
||||
return 0, fmt.Errorf("%w: %v", ErrOutboundPacketTooLarge, maxMessageSize)
|
||||
}
|
||||
|
||||
if s.State() != StreamStateOpen {
|
||||
return 0, ErrStreamClosed
|
||||
}
|
||||
|
||||
chunks := s.packetize(p, ppi)
|
||||
n := len(p)
|
||||
err := s.association.sendPayloadData(chunks)
|
||||
if err != nil {
|
||||
return n, ErrStreamClosed
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPayloadData {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
i := uint32(0)
|
||||
remaining := uint32(len(raw))
|
||||
|
||||
// From draft-ietf-rtcweb-data-protocol-09, section 6:
|
||||
// All Data Channel Establishment Protocol messages MUST be sent using
|
||||
// ordered delivery and reliable transmission.
|
||||
unordered := ppi != PayloadTypeWebRTCDCEP && s.unordered
|
||||
|
||||
var chunks []*chunkPayloadData
|
||||
var head *chunkPayloadData
|
||||
for remaining != 0 {
|
||||
fragmentSize := min32(s.association.maxPayloadSize, remaining)
|
||||
|
||||
// Copy the userdata since we'll have to store it until acked
|
||||
// and the caller may re-use the buffer in the mean time
|
||||
userData := make([]byte, fragmentSize)
|
||||
copy(userData, raw[i:i+fragmentSize])
|
||||
|
||||
chunk := &chunkPayloadData{
|
||||
streamIdentifier: s.streamIdentifier,
|
||||
userData: userData,
|
||||
unordered: unordered,
|
||||
beginningFragment: i == 0,
|
||||
endingFragment: remaining-fragmentSize == 0,
|
||||
immediateSack: false,
|
||||
payloadType: ppi,
|
||||
streamSequenceNumber: s.sequenceNumber,
|
||||
head: head,
|
||||
}
|
||||
|
||||
if head == nil {
|
||||
head = chunk
|
||||
}
|
||||
|
||||
chunks = append(chunks, chunk)
|
||||
|
||||
remaining -= fragmentSize
|
||||
i += fragmentSize
|
||||
}
|
||||
|
||||
// RFC 4960 Sec 6.6
|
||||
// Note: When transmitting ordered and unordered data, an endpoint does
|
||||
// not increment its Stream Sequence Number when transmitting a DATA
|
||||
// chunk with U flag set to 1.
|
||||
if !unordered {
|
||||
s.sequenceNumber++
|
||||
}
|
||||
|
||||
s.bufferedAmount += uint64(len(raw))
|
||||
s.log.Tracef("[%s] bufferedAmount = %d", s.name, s.bufferedAmount)
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
// Close closes the write-direction of the stream.
|
||||
// Future calls to Write are not permitted after calling Close.
|
||||
func (s *Stream) Close() error {
|
||||
if sid, resetOutbound := func() (uint16, bool) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.log.Debugf("[%s] Close: state=%s", s.name, s.state.String())
|
||||
|
||||
if s.state == StreamStateOpen {
|
||||
if s.readErr == nil {
|
||||
s.state = StreamStateClosing
|
||||
} else {
|
||||
s.state = StreamStateClosed
|
||||
}
|
||||
s.log.Debugf("[%s] state change: open => %s", s.name, s.state.String())
|
||||
return s.streamIdentifier, true
|
||||
}
|
||||
return s.streamIdentifier, false
|
||||
}(); resetOutbound {
|
||||
// Reset the outgoing stream
|
||||
// https://tools.ietf.org/html/rfc6525
|
||||
return s.association.sendResetRequest(sid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BufferedAmount returns the number of bytes of data currently queued to be sent over this stream.
|
||||
func (s *Stream) BufferedAmount() uint64 {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.bufferedAmount
|
||||
}
|
||||
|
||||
// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing data that is
|
||||
// considered "low." Defaults to 0.
|
||||
func (s *Stream) BufferedAmountLowThreshold() uint64 {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.bufferedAmountLow
|
||||
}
|
||||
|
||||
// SetBufferedAmountLowThreshold is used to update the threshold.
|
||||
// See BufferedAmountLowThreshold().
|
||||
func (s *Stream) SetBufferedAmountLowThreshold(th uint64) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.bufferedAmountLow = th
|
||||
}
|
||||
|
||||
// OnBufferedAmountLow sets the callback handler which would be called when the number of
|
||||
// bytes of outgoing data buffered is lower than the threshold.
|
||||
func (s *Stream) OnBufferedAmountLow(f func()) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.onBufferedAmountLow = f
|
||||
}
|
||||
|
||||
// This method is called by association's readLoop (go-)routine to notify this stream
|
||||
// of the specified amount of outgoing data has been delivered to the peer.
|
||||
func (s *Stream) onBufferReleased(nBytesReleased int) {
|
||||
if nBytesReleased <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
|
||||
fromAmount := s.bufferedAmount
|
||||
|
||||
if s.bufferedAmount < uint64(nBytesReleased) {
|
||||
s.bufferedAmount = 0
|
||||
s.log.Errorf("[%s] released buffer size %d should be <= %d",
|
||||
s.name, nBytesReleased, s.bufferedAmount)
|
||||
} else {
|
||||
s.bufferedAmount -= uint64(nBytesReleased)
|
||||
}
|
||||
|
||||
s.log.Tracef("[%s] bufferedAmount = %d", s.name, s.bufferedAmount)
|
||||
|
||||
if s.onBufferedAmountLow != nil && fromAmount > s.bufferedAmountLow && s.bufferedAmount <= s.bufferedAmountLow {
|
||||
f := s.onBufferedAmountLow
|
||||
s.lock.Unlock()
|
||||
f()
|
||||
return
|
||||
}
|
||||
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Stream) getNumBytesInReassemblyQueue() int {
|
||||
// No lock is required as it reads the size with atomic load function.
|
||||
return s.reassemblyQueue.getNumBytes()
|
||||
}
|
||||
|
||||
func (s *Stream) onInboundStreamReset() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.log.Debugf("[%s] onInboundStreamReset: state=%s", s.name, s.state.String())
|
||||
|
||||
// No more inbound data to read. Unblock the read with io.EOF.
|
||||
// This should cause DCEP layer (datachannel package) to call Close() which
|
||||
// will reset outgoing stream also.
|
||||
|
||||
// See RFC 8831 section 6.7:
|
||||
// if one side decides to close the data channel, it resets the corresponding
|
||||
// outgoing stream. When the peer sees that an incoming stream was
|
||||
// reset, it also resets its corresponding outgoing stream. Once this
|
||||
// is completed, the data channel is closed.
|
||||
|
||||
s.readErr = io.EOF
|
||||
s.readNotifier.Broadcast()
|
||||
|
||||
if s.state == StreamStateClosing {
|
||||
s.log.Debugf("[%s] state change: closing => closed", s.name)
|
||||
s.state = StreamStateClosed
|
||||
}
|
||||
}
|
||||
|
||||
// State return the stream state.
|
||||
func (s *Stream) State() StreamState {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.state
|
||||
}
|
||||
61
server/vendor/github.com/pion/sctp/util.go
generated
vendored
Normal file
61
server/vendor/github.com/pion/sctp/util.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package sctp
|
||||
|
||||
const (
|
||||
paddingMultiple = 4
|
||||
)
|
||||
|
||||
func getPadding(l int) int {
|
||||
return (paddingMultiple - (l % paddingMultiple)) % paddingMultiple
|
||||
}
|
||||
|
||||
func padByte(in []byte, cnt int) []byte {
|
||||
if cnt < 0 {
|
||||
cnt = 0
|
||||
}
|
||||
padding := make([]byte, cnt)
|
||||
return append(in, padding...)
|
||||
}
|
||||
|
||||
// Serial Number Arithmetic (RFC 1982)
|
||||
func sna32LT(i1, i2 uint32) bool {
|
||||
return (i1 < i2 && i2-i1 < 1<<31) || (i1 > i2 && i1-i2 > 1<<31)
|
||||
}
|
||||
|
||||
func sna32LTE(i1, i2 uint32) bool {
|
||||
return i1 == i2 || sna32LT(i1, i2)
|
||||
}
|
||||
|
||||
func sna32GT(i1, i2 uint32) bool {
|
||||
return (i1 < i2 && (i2-i1) >= 1<<31) || (i1 > i2 && (i1-i2) <= 1<<31)
|
||||
}
|
||||
|
||||
func sna32GTE(i1, i2 uint32) bool {
|
||||
return i1 == i2 || sna32GT(i1, i2)
|
||||
}
|
||||
|
||||
func sna32EQ(i1, i2 uint32) bool {
|
||||
return i1 == i2
|
||||
}
|
||||
|
||||
func sna16LT(i1, i2 uint16) bool {
|
||||
return (i1 < i2 && (i2-i1) < 1<<15) || (i1 > i2 && (i1-i2) > 1<<15)
|
||||
}
|
||||
|
||||
func sna16LTE(i1, i2 uint16) bool {
|
||||
return i1 == i2 || sna16LT(i1, i2)
|
||||
}
|
||||
|
||||
func sna16GT(i1, i2 uint16) bool {
|
||||
return (i1 < i2 && (i2-i1) >= 1<<15) || (i1 > i2 && (i1-i2) <= 1<<15)
|
||||
}
|
||||
|
||||
func sna16GTE(i1, i2 uint16) bool {
|
||||
return i1 == i2 || sna16GT(i1, i2)
|
||||
}
|
||||
|
||||
func sna16EQ(i1, i2 uint16) bool {
|
||||
return i1 == i2
|
||||
}
|
||||
Reference in New Issue
Block a user