直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
28
server/vendor/github.com/pion/transport/v2/.gitignore
generated
vendored
Normal file
28
server/vendor/github.com/pion/transport/v2/.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
|
||||
137
server/vendor/github.com/pion/transport/v2/.golangci.yml
generated
vendored
Normal file
137
server/vendor/github.com/pion/transport/v2/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
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
|
||||
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||
- 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
|
||||
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||
- 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:
|
||||
- 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
|
||||
- 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-rules:
|
||||
# Allow complex tests, better to be self contained
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocognit
|
||||
- forbidigo
|
||||
|
||||
# Allow complex main function in examples
|
||||
- path: examples
|
||||
text: "of func `main` is high"
|
||||
linters:
|
||||
- gocognit
|
||||
|
||||
# Allow forbidden identifiers in examples
|
||||
- path: examples
|
||||
linters:
|
||||
- forbidigo
|
||||
|
||||
# Allow forbidden identifiers in CLI commands
|
||||
- path: cmd
|
||||
linters:
|
||||
- forbidigo
|
||||
|
||||
run:
|
||||
skip-dirs-use-default: false
|
||||
5
server/vendor/github.com/pion/transport/v2/.goreleaser.yml
generated
vendored
Normal file
5
server/vendor/github.com/pion/transport/v2/.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
|
||||
29
server/vendor/github.com/pion/transport/v2/AUTHORS.txt
generated
vendored
Normal file
29
server/vendor/github.com/pion/transport/v2/AUTHORS.txt
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# Thank you to everyone that made Pion possible. If you are interested in contributing
|
||||
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
|
||||
#
|
||||
# This file is auto generated, using git to list all individuals contributors.
|
||||
# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
|
||||
Adrian Cable <adrian.cable@gmail.com>
|
||||
Atsushi Watanabe <atsushi.w@ieee.org>
|
||||
backkem <mail@backkem.me>
|
||||
cnderrauber <zengjie9004@gmail.com>
|
||||
Daniel Mangum <georgedanielmangum@gmail.com>
|
||||
Hugo Arregui <hugo.arregui@gmail.com>
|
||||
Jeremiah Millay <jmillay@fastly.com>
|
||||
Jozef Kralik <jojo.lwin@gmail.com>
|
||||
Juliusz Chroboczek <jch@irif.fr>
|
||||
Luke Curley <kixelated@gmail.com>
|
||||
Mathis Engelbart <mathis.engelbart@gmail.com>
|
||||
OrlandoCo <luisorlando.co@gmail.com>
|
||||
Sean DuBois <duboisea@justin.tv>
|
||||
Sean DuBois <duboisea@twitch.tv>
|
||||
Sean DuBois <seaduboi@amazon.com>
|
||||
Sean DuBois <sean@siobud.com>
|
||||
Steffen Vogel <post@steffenvogel.de>
|
||||
Winlin <winlin@vip.126.com>
|
||||
Woodrow Douglass <wdouglass@carnegierobotics.com>
|
||||
Yutaka Takeda <yt0916@gmail.com>
|
||||
ZHENK <chengzhenyang@gmail.com>
|
||||
|
||||
# List of contributors not appearing in Git history
|
||||
|
||||
9
server/vendor/github.com/pion/transport/v2/LICENSE
generated
vendored
Normal file
9
server/vendor/github.com/pion/transport/v2/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/transport/v2/README.md
generated
vendored
Normal file
34
server/vendor/github.com/pion/transport/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
Pion Transport
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">Transport testing for Pion</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-transport-gray.svg?longCache=true&colorB=brightgreen" alt="Pion transport"></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/transport/test.yaml">
|
||||
<a href="https://pkg.go.dev/github.com/pion/transport"><img src="https://pkg.go.dev/badge/github.com/pion/transport.svg" alt="Go Reference"></a>
|
||||
<a href="https://codecov.io/gh/pion/transport"><img src="https://codecov.io/gh/pion/transport/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/transport"><img src="https://goreportcard.com/badge/github.com/pion/transport" 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: [AUTHORS.txt](./AUTHORS.txt)
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
22
server/vendor/github.com/pion/transport/v2/codecov.yml
generated
vendored
Normal file
22
server/vendor/github.com/pion/transport/v2/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/**/*"
|
||||
177
server/vendor/github.com/pion/transport/v2/connctx/connctx.go
generated
vendored
Normal file
177
server/vendor/github.com/pion/transport/v2/connctx/connctx.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package connctx wraps net.Conn using context.Context.
|
||||
//
|
||||
// Deprecated: use netctx instead.
|
||||
package connctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrClosing is returned on Write to closed connection.
|
||||
var ErrClosing = errors.New("use of closed network connection")
|
||||
|
||||
// Reader is an interface for context controlled reader.
|
||||
type Reader interface {
|
||||
ReadContext(context.Context, []byte) (int, error)
|
||||
}
|
||||
|
||||
// Writer is an interface for context controlled writer.
|
||||
type Writer interface {
|
||||
WriteContext(context.Context, []byte) (int, error)
|
||||
}
|
||||
|
||||
// ReadWriter is a composite of ReadWriter.
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
|
||||
// ConnCtx is a wrapper of net.Conn using context.Context.
|
||||
type ConnCtx interface {
|
||||
Reader
|
||||
Writer
|
||||
io.Closer
|
||||
LocalAddr() net.Addr
|
||||
RemoteAddr() net.Addr
|
||||
Conn() net.Conn
|
||||
}
|
||||
|
||||
type connCtx struct {
|
||||
nextConn net.Conn
|
||||
closed chan struct{}
|
||||
closeOnce sync.Once
|
||||
readMu sync.Mutex
|
||||
writeMu sync.Mutex
|
||||
}
|
||||
|
||||
var veryOld = time.Unix(0, 1) //nolint:gochecknoglobals
|
||||
|
||||
// New creates a new ConnCtx wrapping given net.Conn.
|
||||
func New(conn net.Conn) ConnCtx {
|
||||
c := &connCtx{
|
||||
nextConn: conn,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *connCtx) ReadContext(ctx context.Context, b []byte) (int, error) {
|
||||
c.readMu.Lock()
|
||||
defer c.readMu.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
return 0, io.EOF
|
||||
default:
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
var errSetDeadline atomic.Value
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// context canceled
|
||||
if err := c.nextConn.SetReadDeadline(veryOld); err != nil {
|
||||
errSetDeadline.Store(err)
|
||||
return
|
||||
}
|
||||
<-done
|
||||
if err := c.nextConn.SetReadDeadline(time.Time{}); err != nil {
|
||||
errSetDeadline.Store(err)
|
||||
}
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := c.nextConn.Read(b)
|
||||
|
||||
close(done)
|
||||
wg.Wait()
|
||||
if e := ctx.Err(); e != nil && n == 0 {
|
||||
err = e
|
||||
}
|
||||
if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connCtx) WriteContext(ctx context.Context, b []byte) (int, error) {
|
||||
c.writeMu.Lock()
|
||||
defer c.writeMu.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.closed:
|
||||
return 0, ErrClosing
|
||||
default:
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
var errSetDeadline atomic.Value
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// context canceled
|
||||
if err := c.nextConn.SetWriteDeadline(veryOld); err != nil {
|
||||
errSetDeadline.Store(err)
|
||||
return
|
||||
}
|
||||
<-done
|
||||
if err := c.nextConn.SetWriteDeadline(time.Time{}); err != nil {
|
||||
errSetDeadline.Store(err)
|
||||
}
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := c.nextConn.Write(b)
|
||||
|
||||
close(done)
|
||||
wg.Wait()
|
||||
if e := ctx.Err(); e != nil && n == 0 {
|
||||
err = e
|
||||
}
|
||||
if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *connCtx) Close() error {
|
||||
err := c.nextConn.Close()
|
||||
c.closeOnce.Do(func() {
|
||||
c.writeMu.Lock()
|
||||
c.readMu.Lock()
|
||||
close(c.closed)
|
||||
c.readMu.Unlock()
|
||||
c.writeMu.Unlock()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *connCtx) LocalAddr() net.Addr {
|
||||
return c.nextConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *connCtx) RemoteAddr() net.Addr {
|
||||
return c.nextConn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *connCtx) Conn() net.Conn {
|
||||
return c.nextConn
|
||||
}
|
||||
14
server/vendor/github.com/pion/transport/v2/connctx/pipe.go
generated
vendored
Normal file
14
server/vendor/github.com/pion/transport/v2/connctx/pipe.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package connctx
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Pipe creates piped pair of ConnCtx.
|
||||
func Pipe() (ConnCtx, ConnCtx) {
|
||||
ca, cb := net.Pipe()
|
||||
return New(ca), New(cb)
|
||||
}
|
||||
124
server/vendor/github.com/pion/transport/v2/deadline/deadline.go
generated
vendored
Normal file
124
server/vendor/github.com/pion/transport/v2/deadline/deadline.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package deadline provides deadline timer used to implement
|
||||
// net.Conn compatible connection
|
||||
package deadline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type deadlineState uint8
|
||||
|
||||
const (
|
||||
deadlineStopped deadlineState = iota
|
||||
deadlineStarted
|
||||
deadlineExceeded
|
||||
)
|
||||
|
||||
var _ context.Context = (*Deadline)(nil)
|
||||
|
||||
// Deadline signals updatable deadline timer.
|
||||
// Also, it implements context.Context.
|
||||
type Deadline struct {
|
||||
mu sync.RWMutex
|
||||
timer timer
|
||||
done chan struct{}
|
||||
deadline time.Time
|
||||
state deadlineState
|
||||
pending uint8
|
||||
}
|
||||
|
||||
// New creates new deadline timer.
|
||||
func New() *Deadline {
|
||||
return &Deadline{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deadline) timeout() {
|
||||
d.mu.Lock()
|
||||
if d.pending--; d.pending != 0 || d.state != deadlineStarted {
|
||||
d.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
d.state = deadlineExceeded
|
||||
done := d.done
|
||||
d.mu.Unlock()
|
||||
|
||||
close(done)
|
||||
}
|
||||
|
||||
// Set new deadline. Zero value means no deadline.
|
||||
func (d *Deadline) Set(t time.Time) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if d.state == deadlineStarted && d.timer.Stop() {
|
||||
d.pending--
|
||||
}
|
||||
|
||||
d.deadline = t
|
||||
d.pending++
|
||||
|
||||
if d.state == deadlineExceeded {
|
||||
d.done = make(chan struct{})
|
||||
}
|
||||
|
||||
if t.IsZero() {
|
||||
d.pending--
|
||||
d.state = deadlineStopped
|
||||
return
|
||||
}
|
||||
|
||||
if dur := time.Until(t); dur > 0 {
|
||||
d.state = deadlineStarted
|
||||
if d.timer == nil {
|
||||
d.timer = afterFunc(dur, d.timeout)
|
||||
} else {
|
||||
d.timer.Reset(dur)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
d.pending--
|
||||
d.state = deadlineExceeded
|
||||
close(d.done)
|
||||
}
|
||||
|
||||
// Done receives deadline signal.
|
||||
func (d *Deadline) Done() <-chan struct{} {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
return d.done
|
||||
}
|
||||
|
||||
// Err returns context.DeadlineExceeded if the deadline is exceeded.
|
||||
// Otherwise, it returns nil.
|
||||
func (d *Deadline) Err() error {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
if d.state == deadlineExceeded {
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deadline returns current deadline.
|
||||
func (d *Deadline) Deadline() (time.Time, bool) {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
if d.deadline.IsZero() {
|
||||
return d.deadline, false
|
||||
}
|
||||
return d.deadline, true
|
||||
}
|
||||
|
||||
// Value returns nil.
|
||||
func (d *Deadline) Value(interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
13
server/vendor/github.com/pion/transport/v2/deadline/timer.go
generated
vendored
Normal file
13
server/vendor/github.com/pion/transport/v2/deadline/timer.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package deadline
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type timer interface {
|
||||
Stop() bool
|
||||
Reset(time.Duration) bool
|
||||
}
|
||||
15
server/vendor/github.com/pion/transport/v2/deadline/timer_generic.go
generated
vendored
Normal file
15
server/vendor/github.com/pion/transport/v2/deadline/timer_generic.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package deadline
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func afterFunc(d time.Duration, f func()) timer {
|
||||
return time.AfterFunc(d, f)
|
||||
}
|
||||
67
server/vendor/github.com/pion/transport/v2/deadline/timer_js.go
generated
vendored
Normal file
67
server/vendor/github.com/pion/transport/v2/deadline/timer_js.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build js
|
||||
// +build js
|
||||
|
||||
package deadline
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// jsTimer is a timer utility for wasm with a working Reset function.
|
||||
type jsTimer struct {
|
||||
f func()
|
||||
mu sync.Mutex
|
||||
timer *time.Timer
|
||||
version uint64
|
||||
started bool
|
||||
}
|
||||
|
||||
func afterFunc(d time.Duration, f func()) timer {
|
||||
t := &jsTimer{f: f}
|
||||
t.Reset(d)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *jsTimer) Stop() bool {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.version++
|
||||
t.timer.Stop()
|
||||
|
||||
started := t.started
|
||||
t.started = false
|
||||
return started
|
||||
}
|
||||
|
||||
func (t *jsTimer) Reset(d time.Duration) bool {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if t.timer != nil {
|
||||
t.timer.Stop()
|
||||
}
|
||||
|
||||
t.version++
|
||||
version := t.version
|
||||
t.timer = time.AfterFunc(d, func() {
|
||||
t.mu.Lock()
|
||||
if version != t.version {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
t.started = false
|
||||
t.mu.Unlock()
|
||||
|
||||
t.f()
|
||||
})
|
||||
|
||||
started := t.started
|
||||
t.started = true
|
||||
return started
|
||||
}
|
||||
418
server/vendor/github.com/pion/transport/v2/net.go
generated
vendored
Normal file
418
server/vendor/github.com/pion/transport/v2/net.go
generated
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package transport implements various networking related
|
||||
// functions used throughout the Pion modules.
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoAddressAssigned ...
|
||||
ErrNoAddressAssigned = errors.New("no address assigned")
|
||||
// ErrNotSupported ...
|
||||
ErrNotSupported = errors.New("not supported yey")
|
||||
// ErrInterfaceNotFound ...
|
||||
ErrInterfaceNotFound = errors.New("interface not found")
|
||||
// ErrNotUDPAddress ...
|
||||
ErrNotUDPAddress = errors.New("not a UDP address")
|
||||
)
|
||||
|
||||
// Net is an interface providing common networking functions which are
|
||||
// similar to the functions provided by standard net package.
|
||||
type Net interface {
|
||||
// ListenPacket announces on the local network address.
|
||||
//
|
||||
// The network must be "udp", "udp4", "udp6", "unixgram", or an IP
|
||||
// transport. The IP transports are "ip", "ip4", or "ip6" followed by
|
||||
// a colon and a literal protocol number or a protocol name, as in
|
||||
// "ip:1" or "ip:icmp".
|
||||
//
|
||||
// For UDP and IP networks, if the host in the address parameter is
|
||||
// empty or a literal unspecified IP address, ListenPacket listens on
|
||||
// all available IP addresses of the local system except multicast IP
|
||||
// addresses.
|
||||
// To only use IPv4, use network "udp4" or "ip4:proto".
|
||||
// The address can use a host name, but this is not recommended,
|
||||
// because it will create a listener for at most one of the host's IP
|
||||
// addresses.
|
||||
// If the port in the address parameter is empty or "0", as in
|
||||
// "127.0.0.1:" or "[::1]:0", a port number is automatically chosen.
|
||||
// The LocalAddr method of PacketConn can be used to discover the
|
||||
// chosen port.
|
||||
//
|
||||
// See func Dial for a description of the network and address
|
||||
// parameters.
|
||||
//
|
||||
// ListenPacket uses context.Background internally; to specify the context, use
|
||||
// ListenConfig.ListenPacket.
|
||||
ListenPacket(network string, address string) (net.PacketConn, error)
|
||||
|
||||
// ListenUDP acts like ListenPacket for UDP networks.
|
||||
//
|
||||
// The network must be a UDP network name; see func Dial for details.
|
||||
//
|
||||
// If the IP field of laddr is nil or an unspecified IP address,
|
||||
// ListenUDP listens on all available IP addresses of the local system
|
||||
// except multicast IP addresses.
|
||||
// If the Port field of laddr is 0, a port number is automatically
|
||||
// chosen.
|
||||
ListenUDP(network string, locAddr *net.UDPAddr) (UDPConn, error)
|
||||
|
||||
// ListenTCP acts like Listen for TCP networks.
|
||||
//
|
||||
// The network must be a TCP network name; see func Dial for details.
|
||||
//
|
||||
// If the IP field of laddr is nil or an unspecified IP address,
|
||||
// ListenTCP listens on all available unicast and anycast IP addresses
|
||||
// of the local system.
|
||||
// If the Port field of laddr is 0, a port number is automatically
|
||||
// chosen.
|
||||
ListenTCP(network string, laddr *net.TCPAddr) (TCPListener, error)
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
//
|
||||
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
|
||||
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
|
||||
// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
|
||||
// "unixpacket".
|
||||
//
|
||||
// For TCP and UDP networks, the address has the form "host:port".
|
||||
// The host must be a literal IP address, or a host name that can be
|
||||
// resolved to IP addresses.
|
||||
// The port must be a literal port number or a service name.
|
||||
// If the host is a literal IPv6 address it must be enclosed in square
|
||||
// brackets, as in "[2001:db8::1]:80" or "[fe80::1%zone]:80".
|
||||
// The zone specifies the scope of the literal IPv6 address as defined
|
||||
// in RFC 4007.
|
||||
// The functions JoinHostPort and SplitHostPort manipulate a pair of
|
||||
// host and port in this form.
|
||||
// When using TCP, and the host resolves to multiple IP addresses,
|
||||
// Dial will try each IP address in order until one succeeds.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// Dial("tcp", "golang.org:http")
|
||||
// Dial("tcp", "192.0.2.1:http")
|
||||
// Dial("tcp", "198.51.100.1:80")
|
||||
// Dial("udp", "[2001:db8::1]:domain")
|
||||
// Dial("udp", "[fe80::1%lo0]:53")
|
||||
// Dial("tcp", ":80")
|
||||
//
|
||||
// For IP networks, the network must be "ip", "ip4" or "ip6" followed
|
||||
// by a colon and a literal protocol number or a protocol name, and
|
||||
// the address has the form "host". The host must be a literal IP
|
||||
// address or a literal IPv6 address with zone.
|
||||
// It depends on each operating system how the operating system
|
||||
// behaves with a non-well known protocol number such as "0" or "255".
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// Dial("ip4:1", "192.0.2.1")
|
||||
// Dial("ip6:ipv6-icmp", "2001:db8::1")
|
||||
// Dial("ip6:58", "fe80::1%lo0")
|
||||
//
|
||||
// For TCP, UDP and IP networks, if the host is empty or a literal
|
||||
// unspecified IP address, as in ":80", "0.0.0.0:80" or "[::]:80" for
|
||||
// TCP and UDP, "", "0.0.0.0" or "::" for IP, the local system is
|
||||
// assumed.
|
||||
//
|
||||
// For Unix networks, the address must be a file system path.
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
|
||||
// DialUDP acts like Dial for UDP networks.
|
||||
//
|
||||
// The network must be a UDP network name; see func Dial for details.
|
||||
//
|
||||
// If laddr is nil, a local address is automatically chosen.
|
||||
// If the IP field of raddr is nil or an unspecified IP address, the
|
||||
// local system is assumed.
|
||||
DialUDP(network string, laddr, raddr *net.UDPAddr) (UDPConn, error)
|
||||
|
||||
// DialTCP acts like Dial for TCP networks.
|
||||
//
|
||||
// The network must be a TCP network name; see func Dial for details.
|
||||
//
|
||||
// If laddr is nil, a local address is automatically chosen.
|
||||
// If the IP field of raddr is nil or an unspecified IP address, the
|
||||
// local system is assumed.
|
||||
DialTCP(network string, laddr, raddr *net.TCPAddr) (TCPConn, error)
|
||||
|
||||
// ResolveIPAddr returns an address of IP end point.
|
||||
//
|
||||
// The network must be an IP network name.
|
||||
//
|
||||
// If the host in the address parameter is not a literal IP address,
|
||||
// ResolveIPAddr resolves the address to an address of IP end point.
|
||||
// Otherwise, it parses the address as a literal IP address.
|
||||
// The address parameter can use a host name, but this is not
|
||||
// recommended, because it will return at most one of the host name's
|
||||
// IP addresses.
|
||||
//
|
||||
// See func Dial for a description of the network and address
|
||||
// parameters.
|
||||
ResolveIPAddr(network, address string) (*net.IPAddr, error)
|
||||
|
||||
// ResolveUDPAddr returns an address of UDP end point.
|
||||
//
|
||||
// The network must be a UDP network name.
|
||||
//
|
||||
// If the host in the address parameter is not a literal IP address or
|
||||
// the port is not a literal port number, ResolveUDPAddr resolves the
|
||||
// address to an address of UDP end point.
|
||||
// Otherwise, it parses the address as a pair of literal IP address
|
||||
// and port number.
|
||||
// The address parameter can use a host name, but this is not
|
||||
// recommended, because it will return at most one of the host name's
|
||||
// IP addresses.
|
||||
//
|
||||
// See func Dial for a description of the network and address
|
||||
// parameters.
|
||||
ResolveUDPAddr(network, address string) (*net.UDPAddr, error)
|
||||
|
||||
// ResolveTCPAddr returns an address of TCP end point.
|
||||
//
|
||||
// The network must be a TCP network name.
|
||||
//
|
||||
// If the host in the address parameter is not a literal IP address or
|
||||
// the port is not a literal port number, ResolveTCPAddr resolves the
|
||||
// address to an address of TCP end point.
|
||||
// Otherwise, it parses the address as a pair of literal IP address
|
||||
// and port number.
|
||||
// The address parameter can use a host name, but this is not
|
||||
// recommended, because it will return at most one of the host name's
|
||||
// IP addresses.
|
||||
//
|
||||
// See func Dial for a description of the network and address
|
||||
// parameters.
|
||||
ResolveTCPAddr(network, address string) (*net.TCPAddr, error)
|
||||
|
||||
// Interfaces returns a list of the system's network interfaces.
|
||||
Interfaces() ([]*Interface, error)
|
||||
|
||||
// InterfaceByIndex returns the interface specified by index.
|
||||
//
|
||||
// On Solaris, it returns one of the logical network interfaces
|
||||
// sharing the logical data link; for more precision use
|
||||
// InterfaceByName.
|
||||
InterfaceByIndex(index int) (*Interface, error)
|
||||
|
||||
// InterfaceByName returns the interface specified by name.
|
||||
InterfaceByName(name string) (*Interface, error)
|
||||
|
||||
// The following functions are extensions to Go's standard net package
|
||||
|
||||
CreateDialer(dialer *net.Dialer) Dialer
|
||||
}
|
||||
|
||||
// Dialer is identical to net.Dialer excepts that its methods
|
||||
// (Dial, DialContext) are overridden to use the Net interface.
|
||||
// Use vnet.CreateDialer() to create an instance of this Dialer.
|
||||
type Dialer interface {
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// UDPConn is packet-oriented connection for UDP.
|
||||
type UDPConn interface {
|
||||
// Close closes the connection.
|
||||
// Any blocked Read or Write operations will be unblocked and return errors.
|
||||
Close() error
|
||||
|
||||
// LocalAddr returns the local network address, if known.
|
||||
LocalAddr() net.Addr
|
||||
|
||||
// RemoteAddr returns the remote network address, if known.
|
||||
RemoteAddr() net.Addr
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection. It is equivalent to calling both
|
||||
// SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations
|
||||
// fail instead of blocking. The deadline applies to all future
|
||||
// and pending I/O, not just the immediately following call to
|
||||
// Read or Write. After a deadline has been exceeded, the
|
||||
// connection can be refreshed by setting a deadline in the future.
|
||||
//
|
||||
// If the deadline is exceeded a call to Read or Write or to other
|
||||
// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
|
||||
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
||||
// The error's Timeout method will return true, but note that there
|
||||
// are other possible errors for which the Timeout method will
|
||||
// return true even if the deadline has not been exceeded.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
SetDeadline(t time.Time) error
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls
|
||||
// and any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
SetReadDeadline(t time.Time) error
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls
|
||||
// and any currently-blocked Write call.
|
||||
// Even if write times out, it may return n > 0, indicating that
|
||||
// some of the data was successfully written.
|
||||
// A zero value for t means Write will not time out.
|
||||
SetWriteDeadline(t time.Time) error
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's
|
||||
// receive buffer associated with the connection.
|
||||
SetReadBuffer(bytes int) error
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's
|
||||
// transmit buffer associated with the connection.
|
||||
SetWriteBuffer(bytes int) error
|
||||
|
||||
// Read reads data from the connection.
|
||||
// Read can be made to time out and return an error after a fixed
|
||||
// time limit; see SetDeadline and SetReadDeadline.
|
||||
Read(b []byte) (n int, err error)
|
||||
|
||||
// ReadFrom reads a packet from the connection,
|
||||
// copying the payload into p. It returns the number of
|
||||
// bytes copied into p and the return address that
|
||||
// was on the packet.
|
||||
// It returns the number of bytes read (0 <= n <= len(p))
|
||||
// and any error encountered. Callers should always process
|
||||
// the n > 0 bytes returned before considering the error err.
|
||||
// ReadFrom can be made to time out and return an error after a
|
||||
// fixed time limit; see SetDeadline and SetReadDeadline.
|
||||
ReadFrom(p []byte) (n int, addr net.Addr, err error)
|
||||
|
||||
// ReadFromUDP acts like ReadFrom but returns a UDPAddr.
|
||||
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
|
||||
|
||||
// ReadMsgUDP reads a message from c, copying the payload into b and
|
||||
// the associated out-of-band data into oob. It returns the number of
|
||||
// bytes copied into b, the number of bytes copied into oob, the flags
|
||||
// that were set on the message and the source address of the message.
|
||||
//
|
||||
// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be
|
||||
// used to manipulate IP-level socket options in oob.
|
||||
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
|
||||
|
||||
// Write writes data to the connection.
|
||||
// Write can be made to time out and return an error after a fixed
|
||||
// time limit; see SetDeadline and SetWriteDeadline.
|
||||
Write(b []byte) (n int, err error)
|
||||
|
||||
// WriteTo writes a packet with payload p to addr.
|
||||
// WriteTo can be made to time out and return an Error after a
|
||||
// fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||
// On packet-oriented connections, write timeouts are rare.
|
||||
WriteTo(p []byte, addr net.Addr) (n int, err error)
|
||||
|
||||
// WriteToUDP acts like WriteTo but takes a UDPAddr.
|
||||
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
|
||||
|
||||
// WriteMsgUDP writes a message to addr via c if c isn't connected, or
|
||||
// to c's remote address if c is connected (in which case addr must be
|
||||
// nil). The payload is copied from b and the associated out-of-band
|
||||
// data is copied from oob. It returns the number of payload and
|
||||
// out-of-band bytes written.
|
||||
//
|
||||
// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be
|
||||
// used to manipulate IP-level socket options in oob.
|
||||
WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
|
||||
}
|
||||
|
||||
// TCPConn is an interface for TCP network connections.
|
||||
type TCPConn interface {
|
||||
net.Conn
|
||||
|
||||
// CloseRead shuts down the reading side of the TCP connection.
|
||||
// Most callers should just use Close.
|
||||
CloseRead() error
|
||||
|
||||
// CloseWrite shuts down the writing side of the TCP connection.
|
||||
// Most callers should just use Close.
|
||||
CloseWrite() error
|
||||
|
||||
// ReadFrom implements the io.ReaderFrom ReadFrom method.
|
||||
ReadFrom(r io.Reader) (int64, error)
|
||||
|
||||
// SetLinger sets the behavior of Close on a connection which still
|
||||
// has data waiting to be sent or to be acknowledged.
|
||||
//
|
||||
// If sec < 0 (the default), the operating system finishes sending the
|
||||
// data in the background.
|
||||
//
|
||||
// If sec == 0, the operating system discards any unsent or
|
||||
// unacknowledged data.
|
||||
//
|
||||
// If sec > 0, the data is sent in the background as with sec < 0. On
|
||||
// some operating systems after sec seconds have elapsed any remaining
|
||||
// unsent data may be discarded.
|
||||
SetLinger(sec int) error
|
||||
|
||||
// SetKeepAlive sets whether the operating system should send
|
||||
// keep-alive messages on the connection.
|
||||
SetKeepAlive(keepalive bool) error
|
||||
|
||||
// SetKeepAlivePeriod sets period between keep-alives.
|
||||
SetKeepAlivePeriod(d time.Duration) error
|
||||
|
||||
// SetNoDelay controls whether the operating system should delay
|
||||
// packet transmission in hopes of sending fewer packets (Nagle's
|
||||
// algorithm). The default is true (no delay), meaning that data is
|
||||
// sent as soon as possible after a Write.
|
||||
SetNoDelay(noDelay bool) error
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's
|
||||
// transmit buffer associated with the connection.
|
||||
SetWriteBuffer(bytes int) error
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's
|
||||
// receive buffer associated with the connection.
|
||||
SetReadBuffer(bytes int) error
|
||||
}
|
||||
|
||||
// TCPListener is a TCP network listener. Clients should typically
|
||||
// use variables of type Listener instead of assuming TCP.
|
||||
type TCPListener interface {
|
||||
net.Listener
|
||||
|
||||
// AcceptTCP accepts the next incoming call and returns the new
|
||||
// connection.
|
||||
AcceptTCP() (TCPConn, error)
|
||||
|
||||
// SetDeadline sets the deadline associated with the listener.
|
||||
// A zero time value disables the deadline.
|
||||
SetDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
// Interface wraps a standard net.Interfaces and its assigned addresses
|
||||
type Interface struct {
|
||||
net.Interface
|
||||
addrs []net.Addr
|
||||
}
|
||||
|
||||
// NewInterface creates a new interface based of a standard net.Interface
|
||||
func NewInterface(ifc net.Interface) *Interface {
|
||||
return &Interface{
|
||||
Interface: ifc,
|
||||
addrs: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// AddAddress adds a new address to the interface
|
||||
func (ifc *Interface) AddAddress(addr net.Addr) {
|
||||
ifc.addrs = append(ifc.addrs, addr)
|
||||
}
|
||||
|
||||
// Addrs returns a slice of configured addresses on the interface
|
||||
func (ifc *Interface) Addrs() ([]net.Addr, error) {
|
||||
if len(ifc.addrs) == 0 {
|
||||
return nil, ErrNoAddressAssigned
|
||||
}
|
||||
return ifc.addrs, nil
|
||||
}
|
||||
335
server/vendor/github.com/pion/transport/v2/packetio/buffer.go
generated
vendored
Normal file
335
server/vendor/github.com/pion/transport/v2/packetio/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package packetio provides packet buffer
|
||||
package packetio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/transport/v2/deadline"
|
||||
)
|
||||
|
||||
var errPacketTooBig = errors.New("packet too big")
|
||||
|
||||
// BufferPacketType allow the Buffer to know which packet protocol is writing.
|
||||
type BufferPacketType int
|
||||
|
||||
const (
|
||||
// RTPBufferPacket indicates the Buffer that is handling RTP packets
|
||||
RTPBufferPacket BufferPacketType = 1
|
||||
// RTCPBufferPacket indicates the Buffer that is handling RTCP packets
|
||||
RTCPBufferPacket BufferPacketType = 2
|
||||
)
|
||||
|
||||
// Buffer allows writing packets to an intermediate buffer, which can then be read form.
|
||||
// This is verify similar to bytes.Buffer but avoids combining multiple writes into a single read.
|
||||
type Buffer struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
// this is a circular buffer. If head <= tail, then the useful
|
||||
// data is in the interval [head, tail[. If tail < head, then
|
||||
// the useful data is the union of [head, len[ and [0, tail[.
|
||||
// In order to avoid ambiguity when head = tail, we always leave
|
||||
// an unused byte in the buffer.
|
||||
data []byte
|
||||
head, tail int
|
||||
|
||||
notify chan struct{}
|
||||
waiting bool
|
||||
closed bool
|
||||
|
||||
count int
|
||||
limitCount, limitSize int
|
||||
|
||||
readDeadline *deadline.Deadline
|
||||
}
|
||||
|
||||
const (
|
||||
minSize = 2048
|
||||
cutoffSize = 128 * 1024
|
||||
maxSize = 4 * 1024 * 1024
|
||||
)
|
||||
|
||||
// NewBuffer creates a new Buffer.
|
||||
func NewBuffer() *Buffer {
|
||||
return &Buffer{
|
||||
notify: make(chan struct{}, 1),
|
||||
readDeadline: deadline.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// available returns true if the buffer is large enough to fit a packet
|
||||
// of the given size, taking overhead into account.
|
||||
func (b *Buffer) available(size int) bool {
|
||||
available := b.head - b.tail
|
||||
if available <= 0 {
|
||||
available += len(b.data)
|
||||
}
|
||||
// we interpret head=tail as empty, so always keep a byte free
|
||||
if size+2+1 > available {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// grow increases the size of the buffer. If it returns nil, then the
|
||||
// buffer has been grown. It returns ErrFull if hits a limit.
|
||||
func (b *Buffer) grow() error {
|
||||
var newSize int
|
||||
if len(b.data) < cutoffSize {
|
||||
newSize = 2 * len(b.data)
|
||||
} else {
|
||||
newSize = 5 * len(b.data) / 4
|
||||
}
|
||||
if newSize < minSize {
|
||||
newSize = minSize
|
||||
}
|
||||
if (b.limitSize <= 0 || sizeHardLimit) && newSize > maxSize {
|
||||
newSize = maxSize
|
||||
}
|
||||
|
||||
// one byte slack
|
||||
if b.limitSize > 0 && newSize > b.limitSize+1 {
|
||||
newSize = b.limitSize + 1
|
||||
}
|
||||
|
||||
if newSize <= len(b.data) {
|
||||
return ErrFull
|
||||
}
|
||||
|
||||
newData := make([]byte, newSize)
|
||||
|
||||
var n int
|
||||
if b.head <= b.tail {
|
||||
// data was contiguous
|
||||
n = copy(newData, b.data[b.head:b.tail])
|
||||
} else {
|
||||
// data was discontinuous
|
||||
n = copy(newData, b.data[b.head:])
|
||||
n += copy(newData[n:], b.data[:b.tail])
|
||||
}
|
||||
b.head = 0
|
||||
b.tail = n
|
||||
b.data = newData
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write appends a copy of the packet data to the buffer.
|
||||
// Returns ErrFull if the packet doesn't fit.
|
||||
//
|
||||
// Note that the packet size is limited to 65536 bytes since v0.11.0 due to the internal data structure.
|
||||
func (b *Buffer) Write(packet []byte) (int, error) {
|
||||
if len(packet) >= 0x10000 {
|
||||
return 0, errPacketTooBig
|
||||
}
|
||||
|
||||
b.mutex.Lock()
|
||||
|
||||
if b.closed {
|
||||
b.mutex.Unlock()
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
if (b.limitCount > 0 && b.count >= b.limitCount) ||
|
||||
(b.limitSize > 0 && b.size()+2+len(packet) > b.limitSize) {
|
||||
b.mutex.Unlock()
|
||||
return 0, ErrFull
|
||||
}
|
||||
|
||||
// grow the buffer until the packet fits
|
||||
for !b.available(len(packet)) {
|
||||
err := b.grow()
|
||||
if err != nil {
|
||||
b.mutex.Unlock()
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// store the length of the packet
|
||||
b.data[b.tail] = uint8(len(packet) >> 8)
|
||||
b.tail++
|
||||
if b.tail >= len(b.data) {
|
||||
b.tail = 0
|
||||
}
|
||||
b.data[b.tail] = uint8(len(packet))
|
||||
b.tail++
|
||||
if b.tail >= len(b.data) {
|
||||
b.tail = 0
|
||||
}
|
||||
|
||||
// store the packet
|
||||
n := copy(b.data[b.tail:], packet)
|
||||
b.tail += n
|
||||
if b.tail >= len(b.data) {
|
||||
// we reached the end, wrap around
|
||||
m := copy(b.data, packet[n:])
|
||||
b.tail = m
|
||||
}
|
||||
b.count++
|
||||
|
||||
select {
|
||||
case b.notify <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
b.mutex.Unlock()
|
||||
|
||||
return len(packet), nil
|
||||
}
|
||||
|
||||
// Read populates the given byte slice, returning the number of bytes read.
|
||||
// Blocks until data is available or the buffer is closed.
|
||||
// Returns io.ErrShortBuffer is the packet is too small to copy the Write.
|
||||
// Returns io.EOF if the buffer is closed.
|
||||
func (b *Buffer) Read(packet []byte) (n int, err error) { //nolint:gocognit
|
||||
// Return immediately if the deadline is already exceeded.
|
||||
select {
|
||||
case <-b.readDeadline.Done():
|
||||
return 0, &netError{ErrTimeout, true, true}
|
||||
default:
|
||||
}
|
||||
|
||||
for {
|
||||
b.mutex.Lock()
|
||||
|
||||
if b.head != b.tail {
|
||||
// decode the packet size
|
||||
n1 := b.data[b.head]
|
||||
b.head++
|
||||
if b.head >= len(b.data) {
|
||||
b.head = 0
|
||||
}
|
||||
n2 := b.data[b.head]
|
||||
b.head++
|
||||
if b.head >= len(b.data) {
|
||||
b.head = 0
|
||||
}
|
||||
count := int((uint16(n1) << 8) | uint16(n2))
|
||||
|
||||
// determine the number of bytes we'll actually copy
|
||||
copied := count
|
||||
if copied > len(packet) {
|
||||
copied = len(packet)
|
||||
}
|
||||
|
||||
// copy the data
|
||||
if b.head+copied < len(b.data) {
|
||||
copy(packet, b.data[b.head:b.head+copied])
|
||||
} else {
|
||||
k := copy(packet, b.data[b.head:])
|
||||
copy(packet[k:], b.data[:copied-k])
|
||||
}
|
||||
|
||||
// advance head, discarding any data that wasn't copied
|
||||
b.head += count
|
||||
if b.head >= len(b.data) {
|
||||
b.head -= len(b.data)
|
||||
}
|
||||
|
||||
if b.head == b.tail {
|
||||
// the buffer is empty, reset to beginning
|
||||
// in order to improve cache locality.
|
||||
b.head = 0
|
||||
b.tail = 0
|
||||
}
|
||||
|
||||
b.count--
|
||||
b.mutex.Unlock()
|
||||
|
||||
if copied < count {
|
||||
return copied, io.ErrShortBuffer
|
||||
}
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
if b.closed {
|
||||
b.mutex.Unlock()
|
||||
return 0, io.EOF
|
||||
}
|
||||
b.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case <-b.readDeadline.Done():
|
||||
return 0, &netError{ErrTimeout, true, true}
|
||||
case <-b.notify:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the buffer, unblocking any pending reads.
|
||||
// Data in the buffer can still be read, Read will return io.EOF only when empty.
|
||||
func (b *Buffer) Close() (err error) {
|
||||
b.mutex.Lock()
|
||||
|
||||
if b.closed {
|
||||
b.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
b.waiting = false
|
||||
b.closed = true
|
||||
close(b.notify)
|
||||
b.mutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count returns the number of packets in the buffer.
|
||||
func (b *Buffer) Count() int {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
return b.count
|
||||
}
|
||||
|
||||
// SetLimitCount controls the maximum number of packets that can be buffered.
|
||||
// Causes Write to return ErrFull when this limit is reached.
|
||||
// A zero value will disable this limit.
|
||||
func (b *Buffer) SetLimitCount(limit int) {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.limitCount = limit
|
||||
}
|
||||
|
||||
// Size returns the total byte size of packets in the buffer, including
|
||||
// a small amount of administrative overhead.
|
||||
func (b *Buffer) Size() int {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
return b.size()
|
||||
}
|
||||
|
||||
func (b *Buffer) size() int {
|
||||
size := b.tail - b.head
|
||||
if size < 0 {
|
||||
size += len(b.data)
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// SetLimitSize controls the maximum number of bytes that can be buffered.
|
||||
// Causes Write to return ErrFull when this limit is reached.
|
||||
// A zero value means 4MB since v0.11.0.
|
||||
//
|
||||
// User can set packetioSizeHardLimit build tag to enable 4MB hard limit.
|
||||
// When packetioSizeHardLimit build tag is set, SetLimitSize exceeding
|
||||
// the hard limit will be silently discarded.
|
||||
func (b *Buffer) SetLimitSize(limit int) {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
b.limitSize = limit
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for the Read operation.
|
||||
// Setting to zero means no deadline.
|
||||
func (b *Buffer) SetReadDeadline(t time.Time) error {
|
||||
b.readDeadline.Set(t)
|
||||
return nil
|
||||
}
|
||||
30
server/vendor/github.com/pion/transport/v2/packetio/errors.go
generated
vendored
Normal file
30
server/vendor/github.com/pion/transport/v2/packetio/errors.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package packetio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// netError implements net.Error
|
||||
type netError struct {
|
||||
error
|
||||
timeout, temporary bool
|
||||
}
|
||||
|
||||
func (e *netError) Timeout() bool {
|
||||
return e.timeout
|
||||
}
|
||||
|
||||
func (e *netError) Temporary() bool {
|
||||
return e.temporary
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrFull is returned when the buffer has hit the configured limits.
|
||||
ErrFull = errors.New("packetio.Buffer is full, discarding write")
|
||||
|
||||
// ErrTimeout is returned when a deadline has expired
|
||||
ErrTimeout = errors.New("i/o timeout")
|
||||
)
|
||||
9
server/vendor/github.com/pion/transport/v2/packetio/hardlimit.go
generated
vendored
Normal file
9
server/vendor/github.com/pion/transport/v2/packetio/hardlimit.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build packetioSizeHardlimit
|
||||
// +build packetioSizeHardlimit
|
||||
|
||||
package packetio
|
||||
|
||||
const sizeHardLimit = true
|
||||
9
server/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go
generated
vendored
Normal file
9
server/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !packetioSizeHardlimit
|
||||
// +build !packetioSizeHardlimit
|
||||
|
||||
package packetio
|
||||
|
||||
const sizeHardLimit = false
|
||||
6
server/vendor/github.com/pion/transport/v2/renovate.json
generated
vendored
Normal file
6
server/vendor/github.com/pion/transport/v2/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>pion/renovate-config"
|
||||
]
|
||||
}
|
||||
81
server/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go
generated
vendored
Normal file
81
server/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package replaydetector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// fixedBigInt is the fix-sized multi-word integer.
|
||||
type fixedBigInt struct {
|
||||
bits []uint64
|
||||
n uint
|
||||
msbMask uint64
|
||||
}
|
||||
|
||||
// newFixedBigInt creates a new fix-sized multi-word int.
|
||||
func newFixedBigInt(n uint) *fixedBigInt {
|
||||
chunkSize := (n + 63) / 64
|
||||
if chunkSize == 0 {
|
||||
chunkSize = 1
|
||||
}
|
||||
return &fixedBigInt{
|
||||
bits: make([]uint64, chunkSize),
|
||||
n: n,
|
||||
msbMask: (1 << (64 - n%64)) - 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Lsh is the left shift operation.
|
||||
func (s *fixedBigInt) Lsh(n uint) {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
nChunk := int(n / 64)
|
||||
nN := n % 64
|
||||
|
||||
for i := len(s.bits) - 1; i >= 0; i-- {
|
||||
var carry uint64
|
||||
if i-nChunk >= 0 {
|
||||
carry = s.bits[i-nChunk] << nN
|
||||
if i-nChunk-1 >= 0 {
|
||||
carry |= s.bits[i-nChunk-1] >> (64 - nN)
|
||||
}
|
||||
}
|
||||
s.bits[i] = (s.bits[i] << n) | carry
|
||||
}
|
||||
s.bits[len(s.bits)-1] &= s.msbMask
|
||||
}
|
||||
|
||||
// Bit returns i-th bit of the fixedBigInt.
|
||||
func (s *fixedBigInt) Bit(i uint) uint {
|
||||
if i >= s.n {
|
||||
return 0
|
||||
}
|
||||
chunk := i / 64
|
||||
pos := i % 64
|
||||
if s.bits[chunk]&(1<<pos) != 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetBit sets i-th bit to 1.
|
||||
func (s *fixedBigInt) SetBit(i uint) {
|
||||
if i >= s.n {
|
||||
return
|
||||
}
|
||||
chunk := i / 64
|
||||
pos := i % 64
|
||||
s.bits[chunk] |= 1 << pos
|
||||
}
|
||||
|
||||
// String returns string representation of fixedBigInt.
|
||||
func (s *fixedBigInt) String() string {
|
||||
var out string
|
||||
for i := len(s.bits) - 1; i >= 0; i-- {
|
||||
out += fmt.Sprintf("%016X", s.bits[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
119
server/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go
generated
vendored
Normal file
119
server/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package replaydetector provides packet replay detection algorithm.
|
||||
package replaydetector
|
||||
|
||||
// ReplayDetector is the interface of sequence replay detector.
|
||||
type ReplayDetector interface {
|
||||
// Check returns true if given sequence number is not replayed.
|
||||
// Call accept() to mark the packet is received properly.
|
||||
Check(seq uint64) (accept func(), ok bool)
|
||||
}
|
||||
|
||||
type slidingWindowDetector struct {
|
||||
latestSeq uint64
|
||||
maxSeq uint64
|
||||
windowSize uint
|
||||
mask *fixedBigInt
|
||||
}
|
||||
|
||||
// New creates ReplayDetector.
|
||||
// Created ReplayDetector doesn't allow wrapping.
|
||||
// It can handle monotonically increasing sequence number up to
|
||||
// full 64bit number. It is suitable for DTLS replay protection.
|
||||
func New(windowSize uint, maxSeq uint64) ReplayDetector {
|
||||
return &slidingWindowDetector{
|
||||
maxSeq: maxSeq,
|
||||
windowSize: windowSize,
|
||||
mask: newFixedBigInt(windowSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *slidingWindowDetector) Check(seq uint64) (accept func(), ok bool) {
|
||||
if seq > d.maxSeq {
|
||||
// Exceeded upper limit.
|
||||
return func() {}, false
|
||||
}
|
||||
|
||||
if seq <= d.latestSeq {
|
||||
if d.latestSeq >= uint64(d.windowSize)+seq {
|
||||
return func() {}, false
|
||||
}
|
||||
if d.mask.Bit(uint(d.latestSeq-seq)) != 0 {
|
||||
// The sequence number is duplicated.
|
||||
return func() {}, false
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
if seq > d.latestSeq {
|
||||
// Update the head of the window.
|
||||
d.mask.Lsh(uint(seq - d.latestSeq))
|
||||
d.latestSeq = seq
|
||||
}
|
||||
diff := (d.latestSeq - seq) % d.maxSeq
|
||||
d.mask.SetBit(uint(diff))
|
||||
}, true
|
||||
}
|
||||
|
||||
// WithWrap creates ReplayDetector allowing sequence wrapping.
|
||||
// This is suitable for short bit width counter like SRTP and SRTCP.
|
||||
func WithWrap(windowSize uint, maxSeq uint64) ReplayDetector {
|
||||
return &wrappedSlidingWindowDetector{
|
||||
maxSeq: maxSeq,
|
||||
windowSize: windowSize,
|
||||
mask: newFixedBigInt(windowSize),
|
||||
}
|
||||
}
|
||||
|
||||
type wrappedSlidingWindowDetector struct {
|
||||
latestSeq uint64
|
||||
maxSeq uint64
|
||||
windowSize uint
|
||||
mask *fixedBigInt
|
||||
init bool
|
||||
}
|
||||
|
||||
func (d *wrappedSlidingWindowDetector) Check(seq uint64) (accept func(), ok bool) {
|
||||
if seq > d.maxSeq {
|
||||
// Exceeded upper limit.
|
||||
return func() {}, false
|
||||
}
|
||||
if !d.init {
|
||||
if seq != 0 {
|
||||
d.latestSeq = seq - 1
|
||||
} else {
|
||||
d.latestSeq = d.maxSeq
|
||||
}
|
||||
d.init = true
|
||||
}
|
||||
|
||||
diff := int64(d.latestSeq) - int64(seq)
|
||||
// Wrap the number.
|
||||
if diff > int64(d.maxSeq)/2 {
|
||||
diff -= int64(d.maxSeq + 1)
|
||||
} else if diff <= -int64(d.maxSeq)/2 {
|
||||
diff += int64(d.maxSeq + 1)
|
||||
}
|
||||
|
||||
if diff >= int64(d.windowSize) {
|
||||
// Too old.
|
||||
return func() {}, false
|
||||
}
|
||||
if diff >= 0 {
|
||||
if d.mask.Bit(uint(diff)) != 0 {
|
||||
// The sequence number is duplicated.
|
||||
return func() {}, false
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
if diff < 0 {
|
||||
// Update the head of the window.
|
||||
d.mask.Lsh(uint(-diff))
|
||||
d.latestSeq = seq
|
||||
}
|
||||
d.mask.SetBit(uint(d.latestSeq - seq))
|
||||
}, true
|
||||
}
|
||||
168
server/vendor/github.com/pion/transport/v2/stdnet/net.go
generated
vendored
Normal file
168
server/vendor/github.com/pion/transport/v2/stdnet/net.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package stdnet implements the transport.Net interface
|
||||
// using methods from Go's standard net package.
|
||||
package stdnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/wlynxg/anet"
|
||||
)
|
||||
|
||||
const (
|
||||
lo0String = "lo0String"
|
||||
udpString = "udp"
|
||||
)
|
||||
|
||||
// Net is an implementation of the net.Net interface
|
||||
// based on functions of the standard net package.
|
||||
type Net struct {
|
||||
interfaces []*transport.Interface
|
||||
}
|
||||
|
||||
// NewNet creates a new StdNet instance.
|
||||
func NewNet() (*Net, error) {
|
||||
n := &Net{}
|
||||
|
||||
return n, n.UpdateInterfaces()
|
||||
}
|
||||
|
||||
// Compile-time assertion
|
||||
var _ transport.Net = &Net{}
|
||||
|
||||
// UpdateInterfaces updates the internal list of network interfaces
|
||||
// and associated addresses.
|
||||
func (n *Net) UpdateInterfaces() error {
|
||||
ifs := []*transport.Interface{}
|
||||
|
||||
oifs, err := anet.Interfaces()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range oifs {
|
||||
ifc := transport.NewInterface(oifs[i])
|
||||
|
||||
addrs, err := anet.InterfaceAddrsByInterface(&oifs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ifc.AddAddress(addr)
|
||||
}
|
||||
|
||||
ifs = append(ifs, ifc)
|
||||
}
|
||||
|
||||
n.interfaces = ifs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Interfaces returns a slice of interfaces which are available on the
|
||||
// system
|
||||
func (n *Net) Interfaces() ([]*transport.Interface, error) {
|
||||
return n.interfaces, nil
|
||||
}
|
||||
|
||||
// InterfaceByIndex returns the interface specified by index.
|
||||
//
|
||||
// On Solaris, it returns one of the logical network interfaces
|
||||
// sharing the logical data link; for more precision use
|
||||
// InterfaceByName.
|
||||
func (n *Net) InterfaceByIndex(index int) (*transport.Interface, error) {
|
||||
for _, ifc := range n.interfaces {
|
||||
if ifc.Index == index {
|
||||
return ifc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: index=%d", transport.ErrInterfaceNotFound, index)
|
||||
}
|
||||
|
||||
// InterfaceByName returns the interface specified by name.
|
||||
func (n *Net) InterfaceByName(name string) (*transport.Interface, error) {
|
||||
for _, ifc := range n.interfaces {
|
||||
if ifc.Name == name {
|
||||
return ifc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, name)
|
||||
}
|
||||
|
||||
// ListenPacket announces on the local network address.
|
||||
func (n *Net) ListenPacket(network string, address string) (net.PacketConn, error) {
|
||||
return net.ListenPacket(network, address)
|
||||
}
|
||||
|
||||
// ListenUDP acts like ListenPacket for UDP networks.
|
||||
func (n *Net) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
return net.ListenUDP(network, locAddr)
|
||||
}
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
func (n *Net) Dial(network, address string) (net.Conn, error) {
|
||||
return net.Dial(network, address)
|
||||
}
|
||||
|
||||
// DialUDP acts like Dial for UDP networks.
|
||||
func (n *Net) DialUDP(network string, laddr, raddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
return net.DialUDP(network, laddr, raddr)
|
||||
}
|
||||
|
||||
// ResolveIPAddr returns an address of IP end point.
|
||||
func (n *Net) ResolveIPAddr(network, address string) (*net.IPAddr, error) {
|
||||
return net.ResolveIPAddr(network, address)
|
||||
}
|
||||
|
||||
// ResolveUDPAddr returns an address of UDP end point.
|
||||
func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
||||
return net.ResolveUDPAddr(network, address)
|
||||
}
|
||||
|
||||
// ResolveTCPAddr returns an address of TCP end point.
|
||||
func (n *Net) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) {
|
||||
return net.ResolveTCPAddr(network, address)
|
||||
}
|
||||
|
||||
// DialTCP acts like Dial for TCP networks.
|
||||
func (n *Net) DialTCP(network string, laddr, raddr *net.TCPAddr) (transport.TCPConn, error) {
|
||||
return net.DialTCP(network, laddr, raddr)
|
||||
}
|
||||
|
||||
// ListenTCP acts like Listen for TCP networks.
|
||||
func (n *Net) ListenTCP(network string, laddr *net.TCPAddr) (transport.TCPListener, error) {
|
||||
l, err := net.ListenTCP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tcpListener{l}, nil
|
||||
}
|
||||
|
||||
type tcpListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (l tcpListener) AcceptTCP() (transport.TCPConn, error) {
|
||||
return l.TCPListener.AcceptTCP()
|
||||
}
|
||||
|
||||
type stdDialer struct {
|
||||
*net.Dialer
|
||||
}
|
||||
|
||||
func (d stdDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return d.Dialer.Dial(network, address)
|
||||
}
|
||||
|
||||
// CreateDialer creates an instance of vnet.Dialer
|
||||
func (n *Net) CreateDialer(d *net.Dialer) transport.Dialer {
|
||||
return stdDialer{d}
|
||||
}
|
||||
170
server/vendor/github.com/pion/transport/v2/udp/batchconn.go
generated
vendored
Normal file
170
server/vendor/github.com/pion/transport/v2/udp/batchconn.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package udp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// BatchWriter represents conn can write messages in batch
|
||||
type BatchWriter interface {
|
||||
WriteBatch(ms []ipv4.Message, flags int) (int, error)
|
||||
}
|
||||
|
||||
// BatchReader represents conn can read messages in batch
|
||||
type BatchReader interface {
|
||||
ReadBatch(msg []ipv4.Message, flags int) (int, error)
|
||||
}
|
||||
|
||||
// BatchPacketConn represents conn can read/write messages in batch
|
||||
type BatchPacketConn interface {
|
||||
BatchWriter
|
||||
BatchReader
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// BatchConn uses ipv4/v6.NewPacketConn to wrap a net.PacketConn to write/read messages in batch,
|
||||
// only available in linux. In other platform, it will use single Write/Read as same as net.Conn.
|
||||
type BatchConn struct {
|
||||
net.PacketConn
|
||||
|
||||
batchConn BatchPacketConn
|
||||
|
||||
batchWriteMutex sync.Mutex
|
||||
batchWriteMessages []ipv4.Message
|
||||
batchWritePos int
|
||||
batchWriteLast time.Time
|
||||
|
||||
batchWriteSize int
|
||||
batchWriteInterval time.Duration
|
||||
|
||||
closed int32
|
||||
}
|
||||
|
||||
// NewBatchConn creates a *BatchConn from net.PacketConn with batch configs.
|
||||
func NewBatchConn(conn net.PacketConn, batchWriteSize int, batchWriteInterval time.Duration) *BatchConn {
|
||||
bc := &BatchConn{
|
||||
PacketConn: conn,
|
||||
batchWriteLast: time.Now(),
|
||||
batchWriteInterval: batchWriteInterval,
|
||||
batchWriteSize: batchWriteSize,
|
||||
batchWriteMessages: make([]ipv4.Message, batchWriteSize),
|
||||
}
|
||||
for i := range bc.batchWriteMessages {
|
||||
bc.batchWriteMessages[i].Buffers = [][]byte{make([]byte, sendMTU)}
|
||||
}
|
||||
|
||||
// batch write only supports linux
|
||||
if runtime.GOOS == "linux" {
|
||||
if pc4 := ipv4.NewPacketConn(conn); pc4 != nil {
|
||||
bc.batchConn = pc4
|
||||
} else if pc6 := ipv6.NewPacketConn(conn); pc6 != nil {
|
||||
bc.batchConn = pc6
|
||||
}
|
||||
}
|
||||
|
||||
if bc.batchConn != nil {
|
||||
go func() {
|
||||
writeTicker := time.NewTicker(batchWriteInterval / 2)
|
||||
defer writeTicker.Stop()
|
||||
for atomic.LoadInt32(&bc.closed) != 1 {
|
||||
<-writeTicker.C
|
||||
bc.batchWriteMutex.Lock()
|
||||
if bc.batchWritePos > 0 && time.Since(bc.batchWriteLast) >= bc.batchWriteInterval {
|
||||
_ = bc.flush()
|
||||
}
|
||||
bc.batchWriteMutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
// Close batchConn and the underlying PacketConn
|
||||
func (c *BatchConn) Close() error {
|
||||
atomic.StoreInt32(&c.closed, 1)
|
||||
c.batchWriteMutex.Lock()
|
||||
if c.batchWritePos > 0 {
|
||||
_ = c.flush()
|
||||
}
|
||||
c.batchWriteMutex.Unlock()
|
||||
if c.batchConn != nil {
|
||||
return c.batchConn.Close()
|
||||
}
|
||||
return c.PacketConn.Close()
|
||||
}
|
||||
|
||||
// WriteTo write message to an UDPAddr, addr should be nil if it is a connected socket.
|
||||
func (c *BatchConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
if c.batchConn == nil {
|
||||
return c.PacketConn.WriteTo(b, addr)
|
||||
}
|
||||
return c.enqueueMessage(b, addr)
|
||||
}
|
||||
|
||||
func (c *BatchConn) enqueueMessage(buf []byte, raddr net.Addr) (int, error) {
|
||||
var err error
|
||||
c.batchWriteMutex.Lock()
|
||||
defer c.batchWriteMutex.Unlock()
|
||||
|
||||
msg := &c.batchWriteMessages[c.batchWritePos]
|
||||
// reset buffers
|
||||
msg.Buffers = msg.Buffers[:1]
|
||||
msg.Buffers[0] = msg.Buffers[0][:cap(msg.Buffers[0])]
|
||||
|
||||
c.batchWritePos++
|
||||
if raddr != nil {
|
||||
msg.Addr = raddr
|
||||
}
|
||||
if n := copy(msg.Buffers[0], buf); n < len(buf) {
|
||||
extraBuffer := make([]byte, len(buf)-n)
|
||||
copy(extraBuffer, buf[n:])
|
||||
msg.Buffers = append(msg.Buffers, extraBuffer)
|
||||
} else {
|
||||
msg.Buffers[0] = msg.Buffers[0][:n]
|
||||
}
|
||||
if c.batchWritePos == c.batchWriteSize {
|
||||
err = c.flush()
|
||||
}
|
||||
return len(buf), err
|
||||
}
|
||||
|
||||
// ReadBatch reads messages in batch, return length of message readed and error.
|
||||
func (c *BatchConn) ReadBatch(msgs []ipv4.Message, flags int) (int, error) {
|
||||
if c.batchConn == nil {
|
||||
n, addr, err := c.PacketConn.ReadFrom(msgs[0].Buffers[0])
|
||||
if err == nil {
|
||||
msgs[0].N = n
|
||||
msgs[0].Addr = addr
|
||||
return 1, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
return c.batchConn.ReadBatch(msgs, flags)
|
||||
}
|
||||
|
||||
func (c *BatchConn) flush() error {
|
||||
var writeErr error
|
||||
var txN int
|
||||
for txN < c.batchWritePos {
|
||||
n, err := c.batchConn.WriteBatch(c.batchWriteMessages[txN:c.batchWritePos], 0)
|
||||
if err != nil {
|
||||
writeErr = err
|
||||
break
|
||||
}
|
||||
txN += n
|
||||
}
|
||||
c.batchWritePos = 0
|
||||
c.batchWriteLast = time.Now()
|
||||
return writeErr
|
||||
}
|
||||
406
server/vendor/github.com/pion/transport/v2/udp/conn.go
generated
vendored
Normal file
406
server/vendor/github.com/pion/transport/v2/udp/conn.go
generated
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package udp provides a connection-oriented listener over a UDP PacketConn
|
||||
package udp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2/deadline"
|
||||
"github.com/pion/transport/v2/packetio"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
const (
|
||||
receiveMTU = 8192
|
||||
sendMTU = 1500
|
||||
defaultListenBacklog = 128 // same as Linux default
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrClosedListener = errors.New("udp: listener closed")
|
||||
ErrListenQueueExceeded = errors.New("udp: listen queue exceeded")
|
||||
ErrInvalidBatchConfig = errors.New("udp: invalid batch config")
|
||||
)
|
||||
|
||||
// listener augments a connection-oriented Listener over a UDP PacketConn
|
||||
type listener struct {
|
||||
pConn net.PacketConn
|
||||
|
||||
readBatchSize int
|
||||
|
||||
accepting atomic.Value // bool
|
||||
acceptCh chan *Conn
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
acceptFilter func([]byte) bool
|
||||
|
||||
connLock sync.Mutex
|
||||
conns map[string]*Conn
|
||||
connWG *sync.WaitGroup
|
||||
|
||||
readWG sync.WaitGroup
|
||||
errClose atomic.Value // error
|
||||
|
||||
readDoneCh chan struct{}
|
||||
errRead atomic.Value // error
|
||||
|
||||
logger logging.LeveledLogger
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
func (l *listener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case c := <-l.acceptCh:
|
||||
l.connWG.Add(1)
|
||||
return c, nil
|
||||
|
||||
case <-l.readDoneCh:
|
||||
err, _ := l.errRead.Load().(error)
|
||||
return nil, err
|
||||
|
||||
case <-l.doneCh:
|
||||
return nil, ErrClosedListener
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
func (l *listener) Close() error {
|
||||
var err error
|
||||
l.doneOnce.Do(func() {
|
||||
l.accepting.Store(false)
|
||||
close(l.doneCh)
|
||||
|
||||
l.connLock.Lock()
|
||||
// Close unaccepted connections
|
||||
lclose:
|
||||
for {
|
||||
select {
|
||||
case c := <-l.acceptCh:
|
||||
close(c.doneCh)
|
||||
delete(l.conns, c.rAddr.String())
|
||||
|
||||
default:
|
||||
break lclose
|
||||
}
|
||||
}
|
||||
nConns := len(l.conns)
|
||||
l.connLock.Unlock()
|
||||
|
||||
l.connWG.Done()
|
||||
|
||||
if nConns == 0 {
|
||||
// Wait if this is the final connection
|
||||
l.readWG.Wait()
|
||||
if errClose, ok := l.errClose.Load().(error); ok {
|
||||
err = errClose
|
||||
}
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *listener) Addr() net.Addr {
|
||||
return l.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// BatchIOConfig indicates config to batch read/write packets,
|
||||
// it will use ReadBatch/WriteBatch to improve throughput for UDP.
|
||||
type BatchIOConfig struct {
|
||||
Enable bool
|
||||
// ReadBatchSize indicates the maximum number of packets to be read in one batch, a batch size less than 2 means
|
||||
// disable read batch.
|
||||
ReadBatchSize int
|
||||
// WriteBatchSize indicates the maximum number of packets to be written in one batch
|
||||
WriteBatchSize int
|
||||
// WriteBatchInterval indicates the maximum interval to wait before writing packets in one batch
|
||||
// small interval will reduce latency/jitter, but increase the io count.
|
||||
WriteBatchInterval time.Duration
|
||||
}
|
||||
|
||||
// ListenConfig stores options for listening to an address.
|
||||
type ListenConfig struct {
|
||||
// Backlog defines the maximum length of the queue of pending
|
||||
// connections. It is equivalent of the backlog argument of
|
||||
// POSIX listen function.
|
||||
// If a connection request arrives when the queue is full,
|
||||
// the request will be silently discarded, unlike TCP.
|
||||
// Set zero to use default value 128 which is same as Linux default.
|
||||
Backlog int
|
||||
|
||||
// AcceptFilter determines whether the new conn should be made for
|
||||
// the incoming packet. If not set, any packet creates new conn.
|
||||
AcceptFilter func([]byte) bool
|
||||
|
||||
// ReadBufferSize sets the size of the operating system's
|
||||
// receive buffer associated with the listener.
|
||||
ReadBufferSize int
|
||||
|
||||
// WriteBufferSize sets the size of the operating system's
|
||||
// send buffer associated with the connection.
|
||||
WriteBufferSize int
|
||||
|
||||
Batch BatchIOConfig
|
||||
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
// Listen creates a new listener based on the ListenConfig.
|
||||
func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
|
||||
if lc.Backlog == 0 {
|
||||
lc.Backlog = defaultListenBacklog
|
||||
}
|
||||
|
||||
if lc.Batch.Enable && (lc.Batch.WriteBatchSize <= 0 || lc.Batch.WriteBatchInterval <= 0) {
|
||||
return nil, ErrInvalidBatchConfig
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lc.ReadBufferSize > 0 {
|
||||
_ = conn.SetReadBuffer(lc.ReadBufferSize)
|
||||
}
|
||||
if lc.WriteBufferSize > 0 {
|
||||
_ = conn.SetWriteBuffer(lc.WriteBufferSize)
|
||||
}
|
||||
|
||||
loggerFactory := lc.LoggerFactory
|
||||
if loggerFactory == nil {
|
||||
loggerFactory = logging.NewDefaultLoggerFactory()
|
||||
}
|
||||
|
||||
logger := loggerFactory.NewLogger("transport")
|
||||
|
||||
l := &listener{
|
||||
pConn: conn,
|
||||
acceptCh: make(chan *Conn, lc.Backlog),
|
||||
conns: make(map[string]*Conn),
|
||||
doneCh: make(chan struct{}),
|
||||
acceptFilter: lc.AcceptFilter,
|
||||
connWG: &sync.WaitGroup{},
|
||||
readDoneCh: make(chan struct{}),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
if lc.Batch.Enable {
|
||||
l.pConn = NewBatchConn(conn, lc.Batch.WriteBatchSize, lc.Batch.WriteBatchInterval)
|
||||
l.readBatchSize = lc.Batch.ReadBatchSize
|
||||
}
|
||||
|
||||
l.accepting.Store(true)
|
||||
l.connWG.Add(1)
|
||||
l.readWG.Add(2) // wait readLoop and Close execution routine
|
||||
|
||||
go l.readLoop()
|
||||
go func() {
|
||||
l.connWG.Wait()
|
||||
if err := l.pConn.Close(); err != nil {
|
||||
l.errClose.Store(err)
|
||||
}
|
||||
l.readWG.Done()
|
||||
}()
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Listen creates a new listener using default ListenConfig.
|
||||
func Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
|
||||
return (&ListenConfig{}).Listen(network, laddr)
|
||||
}
|
||||
|
||||
// readLoop has to tasks:
|
||||
// 1. Dispatching incoming packets to the correct Conn.
|
||||
// It can therefore not be ended until all Conns are closed.
|
||||
// 2. Creating a new Conn when receiving from a new remote.
|
||||
func (l *listener) readLoop() {
|
||||
defer l.readWG.Done()
|
||||
defer close(l.readDoneCh)
|
||||
|
||||
if br, ok := l.pConn.(BatchReader); ok && l.readBatchSize > 1 {
|
||||
l.readBatch(br)
|
||||
} else {
|
||||
l.read()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) readBatch(br BatchReader) {
|
||||
msgs := make([]ipv4.Message, l.readBatchSize)
|
||||
for i := range msgs {
|
||||
msg := &msgs[i]
|
||||
msg.Buffers = [][]byte{make([]byte, receiveMTU)}
|
||||
msg.OOB = make([]byte, 40)
|
||||
}
|
||||
for {
|
||||
n, err := br.ReadBatch(msgs, 0)
|
||||
if err != nil {
|
||||
l.errRead.Store(err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
l.dispatchMsg(msgs[i].Addr, msgs[i].Buffers[0][:msgs[i].N])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) read() {
|
||||
buf := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, raddr, err := l.pConn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
l.errRead.Store(err)
|
||||
l.logger.Tracef("error reading from connection err=%v", err)
|
||||
return
|
||||
}
|
||||
l.dispatchMsg(raddr, buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) dispatchMsg(addr net.Addr, buf []byte) {
|
||||
conn, ok, err := l.getConn(addr, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
_, err := conn.buffer.Write(buf)
|
||||
if err != nil {
|
||||
l.logger.Tracef("error dispatching message addr=%v err=%v", addr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) getConn(raddr net.Addr, buf []byte) (*Conn, bool, error) {
|
||||
l.connLock.Lock()
|
||||
defer l.connLock.Unlock()
|
||||
conn, ok := l.conns[raddr.String()]
|
||||
if !ok {
|
||||
if isAccepting, ok := l.accepting.Load().(bool); !isAccepting || !ok {
|
||||
return nil, false, ErrClosedListener
|
||||
}
|
||||
if l.acceptFilter != nil {
|
||||
if !l.acceptFilter(buf) {
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
conn = l.newConn(raddr)
|
||||
select {
|
||||
case l.acceptCh <- conn:
|
||||
l.conns[raddr.String()] = conn
|
||||
default:
|
||||
return nil, false, ErrListenQueueExceeded
|
||||
}
|
||||
}
|
||||
return conn, true, nil
|
||||
}
|
||||
|
||||
// Conn augments a connection-oriented connection over a UDP PacketConn
|
||||
type Conn struct {
|
||||
listener *listener
|
||||
|
||||
rAddr net.Addr
|
||||
|
||||
buffer *packetio.Buffer
|
||||
|
||||
doneCh chan struct{}
|
||||
doneOnce sync.Once
|
||||
|
||||
writeDeadline *deadline.Deadline
|
||||
}
|
||||
|
||||
func (l *listener) newConn(rAddr net.Addr) *Conn {
|
||||
return &Conn{
|
||||
listener: l,
|
||||
rAddr: rAddr,
|
||||
buffer: packetio.NewBuffer(),
|
||||
doneCh: make(chan struct{}),
|
||||
writeDeadline: deadline.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads from c into p
|
||||
func (c *Conn) Read(p []byte) (int, error) {
|
||||
return c.buffer.Read(p)
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes from p to the DTLS connection
|
||||
func (c *Conn) Write(p []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.writeDeadline.Done():
|
||||
return 0, context.DeadlineExceeded
|
||||
default:
|
||||
}
|
||||
return c.listener.pConn.WriteTo(p, c.rAddr)
|
||||
}
|
||||
|
||||
// Close closes the conn and releases any Read calls
|
||||
func (c *Conn) Close() error {
|
||||
var err error
|
||||
c.doneOnce.Do(func() {
|
||||
c.listener.connWG.Done()
|
||||
close(c.doneCh)
|
||||
c.listener.connLock.Lock()
|
||||
delete(c.listener.conns, c.rAddr.String())
|
||||
nConns := len(c.listener.conns)
|
||||
c.listener.connLock.Unlock()
|
||||
|
||||
if isAccepting, ok := c.listener.accepting.Load().(bool); nConns == 0 && !isAccepting && ok {
|
||||
// Wait if this is the final connection
|
||||
c.listener.readWG.Wait()
|
||||
if errClose, ok := c.listener.errClose.Load().(error); ok {
|
||||
err = errClose
|
||||
}
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if errBuf := c.buffer.Close(); errBuf != nil && err == nil {
|
||||
err = errBuf
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// LocalAddr implements net.Conn.LocalAddr
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.listener.pConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr implements net.Conn.RemoteAddr
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
// SetDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
c.writeDeadline.Set(t)
|
||||
return c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.buffer.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements net.Conn.SetDeadline
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
c.writeDeadline.Set(t)
|
||||
// Write deadline of underlying connection should not be changed
|
||||
// since the connection can be shared.
|
||||
return nil
|
||||
}
|
||||
29
server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go
generated
vendored
Normal file
29
server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
// Package xor provides utility functions used by other Pion
|
||||
// packages. AMD64 arch.
|
||||
package xor
|
||||
|
||||
// XorBytes xors the bytes in a and b. The destination should have enough
|
||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
||||
//
|
||||
//revive:disable-next-line
|
||||
func XorBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
_ = dst[n-1]
|
||||
xorBytesSSE2(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2
|
||||
return n
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func xorBytesSSE2(dst, a, b *byte, n int)
|
||||
56
server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s
generated
vendored
Normal file
56
server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytesSSE2(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytesSSE2(SB), NOSPLIT, $0
|
||||
MOVQ dst+0(FP), BX
|
||||
MOVQ a+8(FP), SI
|
||||
MOVQ b+16(FP), CX
|
||||
MOVQ n+24(FP), DX
|
||||
TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned.
|
||||
JNZ not_aligned
|
||||
|
||||
aligned:
|
||||
MOVQ $0, AX // position in slices
|
||||
|
||||
loop16b:
|
||||
MOVOU (SI)(AX*1), X0 // XOR 16byte forwards.
|
||||
MOVOU (CX)(AX*1), X1
|
||||
PXOR X1, X0
|
||||
MOVOU X0, (BX)(AX*1)
|
||||
ADDQ $16, AX
|
||||
CMPQ DX, AX
|
||||
JNE loop16b
|
||||
RET
|
||||
|
||||
loop_1b:
|
||||
SUBQ $1, DX // XOR 1byte backwards.
|
||||
MOVB (SI)(DX*1), DI
|
||||
MOVB (CX)(DX*1), AX
|
||||
XORB AX, DI
|
||||
MOVB DI, (BX)(DX*1)
|
||||
TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b.
|
||||
JNZ loop_1b
|
||||
CMPQ DX, $0 // if len is 0, ret.
|
||||
JE ret
|
||||
TESTQ $15, DX // AND 15 & len, if zero jump to aligned.
|
||||
JZ aligned
|
||||
|
||||
not_aligned:
|
||||
TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b.
|
||||
JNE loop_1b
|
||||
SUBQ $8, DX // XOR 8bytes backwards.
|
||||
MOVQ (SI)(DX*1), DI
|
||||
MOVQ (CX)(DX*1), AX
|
||||
XORQ AX, DI
|
||||
MOVQ DI, (BX)(DX*1)
|
||||
CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned.
|
||||
JGE aligned
|
||||
|
||||
ret:
|
||||
RET
|
||||
60
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go
generated
vendored
Normal file
60
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: 2022 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
// Package xor provides utility functions used by other Pion
|
||||
// packages. ARM arch.
|
||||
package xor
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0))) // nolint:gosec
|
||||
var hasNEON = cpu.ARM.HasNEON // nolint:gochecknoglobals
|
||||
|
||||
func isAligned(a *byte) bool {
|
||||
return uintptr(unsafe.Pointer(a))%uintptr(wordSize) == 0
|
||||
}
|
||||
|
||||
// XorBytes xors the bytes in a and b. The destination should have enough
|
||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
||||
//
|
||||
//revive:disable-next-line
|
||||
func XorBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
// make sure dst has enough space
|
||||
_ = dst[n-1]
|
||||
|
||||
if hasNEON {
|
||||
xorBytesNEON32(&dst[0], &a[0], &b[0], n)
|
||||
} else if isAligned(&dst[0]) && isAligned(&a[0]) && isAligned(&b[0]) {
|
||||
xorBytesARM32(&dst[0], &a[0], &b[0], n)
|
||||
} else {
|
||||
safeXORBytes(dst, a, b, n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// n needs to be smaller or equal than the length of a and b.
|
||||
func safeXORBytes(dst, a, b []byte, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func xorBytesARM32(dst, a, b *byte, n int)
|
||||
|
||||
//go:noescape
|
||||
func xorBytesNEON32(dst, a, b *byte, n int)
|
||||
116
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s
generated
vendored
Normal file
116
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-FileCopyrightText: 2022 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytesARM32(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytesARM32(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVW dst+0(FP), R0
|
||||
MOVW a+4(FP), R1
|
||||
MOVW b+8(FP), R2
|
||||
MOVW n+12(FP), R3
|
||||
CMP $4, R3
|
||||
BLT less_than4
|
||||
|
||||
loop_4:
|
||||
MOVW.P 4(R1), R4
|
||||
MOVW.P 4(R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVW.P R5, 4(R0)
|
||||
|
||||
SUB $4, R3
|
||||
CMP $4, R3
|
||||
BGE loop_4
|
||||
|
||||
less_than4:
|
||||
CMP $2, R3
|
||||
BLT less_than2
|
||||
MOVH.P 2(R1), R4
|
||||
MOVH.P 2(R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVH.P R5, 2(R0)
|
||||
|
||||
SUB $2, R3
|
||||
|
||||
less_than2:
|
||||
CMP $0, R3
|
||||
BEQ end
|
||||
MOVB (R1), R4
|
||||
MOVB (R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVB R5, (R0)
|
||||
end:
|
||||
RET
|
||||
|
||||
// func xorBytesNEON32(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytesNEON32(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVW dst+0(FP), R0
|
||||
MOVW a+4(FP), R1
|
||||
MOVW b+8(FP), R2
|
||||
MOVW n+12(FP), R3
|
||||
CMP $32, R3
|
||||
BLT less_than32
|
||||
|
||||
loop_32:
|
||||
WORD $0xF421020D // vld1.u8 {q0, q1}, [r1]!
|
||||
WORD $0xF422420D // vld1.u8 {q2, q3}, [r2]!
|
||||
WORD $0xF3004154 // veor q2, q0, q2
|
||||
WORD $0xF3026156 // veor q3, q1, q3
|
||||
WORD $0xF400420D // vst1.u8 {q2, q3}, [r0]!
|
||||
|
||||
SUB $32, R3
|
||||
CMP $32, R3
|
||||
BGE loop_32
|
||||
|
||||
less_than32:
|
||||
CMP $16, R3
|
||||
BLT less_than16
|
||||
WORD $0xF4210A0D // vld1.u8 q0, [r1]!
|
||||
WORD $0xF4222A0D // vld1.u8 q1, [r2]!
|
||||
WORD $0xF3002152 // veor q1, q0, q1
|
||||
WORD $0xF4002A0D // vst1.u8 {q1}, [r0]!
|
||||
|
||||
SUB $16, R3
|
||||
|
||||
less_than16:
|
||||
CMP $8, R3
|
||||
BLT less_than8
|
||||
WORD $0xF421070D // vld1.u8 d0, [r1]!
|
||||
WORD $0xF422170D // vld1.u8 d1, [r2]!
|
||||
WORD $0xF3001111 // veor d1, d0, d1
|
||||
WORD $0xF400170D // vst1.u8 {d1}, [r0]!
|
||||
|
||||
SUB $8, R3
|
||||
|
||||
less_than8:
|
||||
CMP $4, R3
|
||||
BLT less_than4
|
||||
MOVW.P 4(R1), R4
|
||||
MOVW.P 4(R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVW.P R5, 4(R0)
|
||||
|
||||
SUB $4, R3
|
||||
|
||||
less_than4:
|
||||
CMP $2, R3
|
||||
BLT less_than2
|
||||
MOVH.P 2(R1), R4
|
||||
MOVH.P 2(R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVH.P R5, 2(R0)
|
||||
|
||||
SUB $2, R3
|
||||
|
||||
less_than2:
|
||||
CMP $0, R3
|
||||
BEQ end
|
||||
MOVB (R1), R4
|
||||
MOVB (R2), R5
|
||||
EOR R4, R5, R5
|
||||
MOVB R5, (R0)
|
||||
end:
|
||||
RET
|
||||
31
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go
generated
vendored
Normal file
31
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
// Package xor provides utility functions used by other Pion
|
||||
// packages. ARM64 arch.
|
||||
package xor
|
||||
|
||||
// XorBytes xors the bytes in a and b. The destination should have enough
|
||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
||||
//
|
||||
//revive:disable-next-line
|
||||
func XorBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
// make sure dst has enough space
|
||||
_ = dst[n-1]
|
||||
|
||||
xorBytesARM64(&dst[0], &a[0], &b[0], n)
|
||||
return n
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func xorBytesARM64(dst, a, b *byte, n int)
|
||||
69
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s
generated
vendored
Normal file
69
server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: 2020 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !gccgo
|
||||
// +build !gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytesARM64(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytesARM64(SB), NOSPLIT|NOFRAME, $0
|
||||
MOVD dst+0(FP), R0
|
||||
MOVD a+8(FP), R1
|
||||
MOVD b+16(FP), R2
|
||||
MOVD n+24(FP), R3
|
||||
CMP $64, R3
|
||||
BLT tail
|
||||
loop_64:
|
||||
VLD1.P 64(R1), [V0.B16, V1.B16, V2.B16, V3.B16]
|
||||
VLD1.P 64(R2), [V4.B16, V5.B16, V6.B16, V7.B16]
|
||||
VEOR V0.B16, V4.B16, V4.B16
|
||||
VEOR V1.B16, V5.B16, V5.B16
|
||||
VEOR V2.B16, V6.B16, V6.B16
|
||||
VEOR V3.B16, V7.B16, V7.B16
|
||||
VST1.P [V4.B16, V5.B16, V6.B16, V7.B16], 64(R0)
|
||||
SUBS $64, R3
|
||||
CMP $64, R3
|
||||
BGE loop_64
|
||||
tail:
|
||||
// quick end
|
||||
CBZ R3, end
|
||||
TBZ $5, R3, less_than32
|
||||
VLD1.P 32(R1), [V0.B16, V1.B16]
|
||||
VLD1.P 32(R2), [V2.B16, V3.B16]
|
||||
VEOR V0.B16, V2.B16, V2.B16
|
||||
VEOR V1.B16, V3.B16, V3.B16
|
||||
VST1.P [V2.B16, V3.B16], 32(R0)
|
||||
less_than32:
|
||||
TBZ $4, R3, less_than16
|
||||
LDP.P 16(R1), (R11, R12)
|
||||
LDP.P 16(R2), (R13, R14)
|
||||
EOR R11, R13, R13
|
||||
EOR R12, R14, R14
|
||||
STP.P (R13, R14), 16(R0)
|
||||
less_than16:
|
||||
TBZ $3, R3, less_than8
|
||||
MOVD.P 8(R1), R11
|
||||
MOVD.P 8(R2), R12
|
||||
EOR R11, R12, R12
|
||||
MOVD.P R12, 8(R0)
|
||||
less_than8:
|
||||
TBZ $2, R3, less_than4
|
||||
MOVWU.P 4(R1), R13
|
||||
MOVWU.P 4(R2), R14
|
||||
EORW R13, R14, R14
|
||||
MOVWU.P R14, 4(R0)
|
||||
less_than4:
|
||||
TBZ $1, R3, less_than2
|
||||
MOVHU.P 2(R1), R15
|
||||
MOVHU.P 2(R2), R16
|
||||
EORW R15, R16, R16
|
||||
MOVHU.P R16, 2(R0)
|
||||
less_than2:
|
||||
TBZ $0, R3, end
|
||||
MOVBU (R1), R17
|
||||
MOVBU (R2), R19
|
||||
EORW R17, R19, R19
|
||||
MOVBU R19, (R0)
|
||||
end:
|
||||
RET
|
||||
78
server/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go
generated
vendored
Normal file
78
server/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2013 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-FileCopyrightText: 2022 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build (!amd64 && !ppc64 && !ppc64le && !arm64 && !arm) || gccgo
|
||||
// +build !amd64,!ppc64,!ppc64le,!arm64,!arm gccgo
|
||||
|
||||
// Package xor provides utility functions used by other Pion
|
||||
// packages. Generic arch.
|
||||
package xor
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
wordSize = int(unsafe.Sizeof(uintptr(0))) // nolint:gosec
|
||||
supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" // nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
func isAligned(a *byte) bool {
|
||||
return uintptr(unsafe.Pointer(a))%uintptr(wordSize) == 0
|
||||
}
|
||||
|
||||
// XorBytes xors the bytes in a and b. The destination should have enough
|
||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
||||
//
|
||||
//revive:disable-next-line
|
||||
func XorBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case supportsUnaligned:
|
||||
fastXORBytes(dst, a, b, n)
|
||||
case isAligned(&dst[0]) && isAligned(&a[0]) && isAligned(&b[0]):
|
||||
fastXORBytes(dst, a, b, n)
|
||||
default:
|
||||
safeXORBytes(dst, a, b, n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// fastXORBytes xors in bulk. It only works on architectures that
|
||||
// support unaligned read/writes.
|
||||
// n needs to be smaller or equal than the length of a and b.
|
||||
func fastXORBytes(dst, a, b []byte, n int) {
|
||||
// Assert dst has enough space
|
||||
_ = dst[n-1]
|
||||
|
||||
w := n / wordSize
|
||||
if w > 0 {
|
||||
dw := *(*[]uintptr)(unsafe.Pointer(&dst)) // nolint:gosec
|
||||
aw := *(*[]uintptr)(unsafe.Pointer(&a)) // nolint:gosec
|
||||
bw := *(*[]uintptr)(unsafe.Pointer(&b)) // nolint:gosec
|
||||
for i := 0; i < w; i++ {
|
||||
dw[i] = aw[i] ^ bw[i]
|
||||
}
|
||||
}
|
||||
|
||||
for i := (n - n%wordSize); i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
}
|
||||
|
||||
// n needs to be smaller or equal than the length of a and b.
|
||||
func safeXORBytes(dst, a, b []byte, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
dst[i] = a[i] ^ b[i]
|
||||
}
|
||||
}
|
||||
29
server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go
generated
vendored
Normal file
29
server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build (ppc64 && !gccgo) || (ppc64le && !gccgo)
|
||||
// +build ppc64,!gccgo ppc64le,!gccgo
|
||||
|
||||
// Package xor provides utility functions used by other Pion
|
||||
// packages. PPC64 arch.
|
||||
package xor
|
||||
|
||||
// XorBytes xors the bytes in a and b. The destination should have enough
|
||||
// space, otherwise xorBytes will panic. Returns the number of bytes xor'd.
|
||||
//
|
||||
//revive:disable-next-line
|
||||
func XorBytes(dst, a, b []byte) int {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
n = len(b)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
_ = dst[n-1]
|
||||
xorBytesVSX(&dst[0], &a[0], &b[0], n)
|
||||
return n
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func xorBytesVSX(dst, a, b *byte, n int)
|
||||
87
server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s
generated
vendored
Normal file
87
server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build (ppc64 && !gccgo) || (ppc64le && !gccgo)
|
||||
//+build ppc64,!gccgo ppc64le,!gccgo
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func xorBytesVSX(dst, a, b *byte, n int)
|
||||
TEXT ·xorBytesVSX(SB), NOSPLIT, $0
|
||||
MOVD dst+0(FP), R3 // R3 = dst
|
||||
MOVD a+8(FP), R4 // R4 = a
|
||||
MOVD b+16(FP), R5 // R5 = b
|
||||
MOVD n+24(FP), R6 // R6 = n
|
||||
|
||||
CMPU R6, $32, CR7 // Check if n ≥ 32 bytes
|
||||
MOVD R0, R8 // R8 = index
|
||||
CMPU R6, $8, CR6 // Check if 8 ≤ n < 32 bytes
|
||||
BLT CR6, small // Smaller than 8
|
||||
BLT CR7, xor16 // Case for 16 ≤ n < 32 bytes
|
||||
|
||||
// Case for n ≥ 32 bytes
|
||||
preloop32:
|
||||
SRD $5, R6, R7 // Setup loop counter
|
||||
MOVD R7, CTR
|
||||
MOVD $16, R10
|
||||
ANDCC $31, R6, R9 // Check for tailing bytes for later
|
||||
loop32:
|
||||
LXVD2X (R4)(R8), VS32 // VS32 = a[i,...,i+15]
|
||||
LXVD2X (R4)(R10), VS34
|
||||
LXVD2X (R5)(R8), VS33 // VS33 = b[i,...,i+15]
|
||||
LXVD2X (R5)(R10), VS35
|
||||
XXLXOR VS32, VS33, VS32 // VS34 = a[] ^ b[]
|
||||
XXLXOR VS34, VS35, VS34
|
||||
STXVD2X VS32, (R3)(R8) // Store to dst
|
||||
STXVD2X VS34, (R3)(R10)
|
||||
ADD $32, R8 // Update index
|
||||
ADD $32, R10
|
||||
BC 16, 0, loop32 // bdnz loop16
|
||||
|
||||
BEQ CR0, done
|
||||
|
||||
MOVD R9, R6
|
||||
CMP R6, $8
|
||||
BLT small
|
||||
xor16:
|
||||
CMP R6, $16
|
||||
BLT xor8
|
||||
LXVD2X (R4)(R8), VS32
|
||||
LXVD2X (R5)(R8), VS33
|
||||
XXLXOR VS32, VS33, VS32
|
||||
STXVD2X VS32, (R3)(R8)
|
||||
ADD $16, R8
|
||||
ADD $-16, R6
|
||||
CMP R6, $8
|
||||
BLT small
|
||||
xor8:
|
||||
// Case for 8 ≤ n < 16 bytes
|
||||
MOVD (R4)(R8), R14 // R14 = a[i,...,i+7]
|
||||
MOVD (R5)(R8), R15 // R15 = b[i,...,i+7]
|
||||
XOR R14, R15, R16 // R16 = a[] ^ b[]
|
||||
SUB $8, R6 // n = n - 8
|
||||
MOVD R16, (R3)(R8) // Store to dst
|
||||
ADD $8, R8
|
||||
|
||||
// Check if we're finished
|
||||
CMP R6, R0
|
||||
BGT small
|
||||
RET
|
||||
|
||||
// Case for n < 8 bytes and tailing bytes from the
|
||||
// previous cases.
|
||||
small:
|
||||
CMP R6, R0
|
||||
BEQ done
|
||||
MOVD R6, CTR // Setup loop counter
|
||||
|
||||
loop:
|
||||
MOVBZ (R4)(R8), R14 // R14 = a[i]
|
||||
MOVBZ (R5)(R8), R15 // R15 = b[i]
|
||||
XOR R14, R15, R16 // R16 = a[i] ^ b[i]
|
||||
MOVB R16, (R3)(R8) // Store to dst
|
||||
ADD $1, R8
|
||||
BC 16, 0, loop // bdnz loop
|
||||
|
||||
done:
|
||||
RET
|
||||
4
server/vendor/github.com/pion/transport/v2/vnet/.gitignore
generated
vendored
Normal file
4
server/vendor/github.com/pion/transport/v2/vnet/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
*.sw[poe]
|
||||
231
server/vendor/github.com/pion/transport/v2/vnet/README.md
generated
vendored
Normal file
231
server/vendor/github.com/pion/transport/v2/vnet/README.md
generated
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
# vnet
|
||||
A virtual network layer for pion.
|
||||
|
||||
## Overview
|
||||
|
||||
### Goals
|
||||
* To make NAT traversal tests easy.
|
||||
* To emulate packet impairment at application level for testing.
|
||||
* To monitor packets at specified arbitrary interfaces.
|
||||
|
||||
### Features
|
||||
* Configurable virtual LAN and WAN
|
||||
* Virtually hosted ICE servers
|
||||
|
||||
### Virtual network components
|
||||
|
||||
#### Top View
|
||||
```
|
||||
......................................
|
||||
: Virtual Network (vnet) :
|
||||
: :
|
||||
+-------+ * 1 +----+ +--------+ :
|
||||
| :App |------------>|:Net|--o<-----|:Router | :
|
||||
+-------+ +----+ | | :
|
||||
+-----------+ * 1 +----+ | | :
|
||||
|:STUNServer|-------->|:Net|--o<-----| | :
|
||||
+-----------+ +----+ | | :
|
||||
+-----------+ * 1 +----+ | | :
|
||||
|:TURNServer|-------->|:Net|--o<-----| | :
|
||||
+-----------+ +----+ [1] | | :
|
||||
: 1 | | 1 <<has>> :
|
||||
: +---<>| |<>----+ [2] :
|
||||
: | +--------+ | :
|
||||
To form | *| v 0..1 :
|
||||
a subnet tree | o [3] +-----+ :
|
||||
: | ^ |:NAT | :
|
||||
: | | +-----+ :
|
||||
: +-------+ :
|
||||
......................................
|
||||
Note:
|
||||
o: NIC (Network Interface Controller)
|
||||
[1]: Net implements NIC interface.
|
||||
[2]: Root router has no NAT. All child routers have a NAT always.
|
||||
[3]: Router implements NIC interface for accesses from the
|
||||
parent router.
|
||||
```
|
||||
|
||||
#### Net
|
||||
Net provides 3 interfaces:
|
||||
* Configuration API (direct)
|
||||
* Network API via Net (equivalent to net.Xxx())
|
||||
* Router access via NIC interface
|
||||
```
|
||||
(Pion module/app, ICE servers, etc.)
|
||||
+-----------+
|
||||
| :App |
|
||||
+-----------+
|
||||
* |
|
||||
| <<uses>>
|
||||
1 v
|
||||
+---------+ 1 * +-----------+ 1 * +-----------+ 1 * +------+
|
||||
..| :Router |----+------>o--| :Net |<>------|:Interface |<>------|:Addr |
|
||||
+---------+ | NIC +-----------+ +-----------+ +------+
|
||||
| <<interface>> (transport.Interface) (net.Addr)
|
||||
|
|
||||
| * +-----------+ 1 * +-----------+ 1 * +------+
|
||||
+------>o--| :Router |<>------|:Interface |<>------|:Addr |
|
||||
NIC +-----------+ +-----------+ +------+
|
||||
<<interface>> (transport.Interface) (net.Addr)
|
||||
```
|
||||
|
||||
> The instance of `Net` will be the one passed around the project.
|
||||
> Net class has public methods for configuration and for application use.
|
||||
|
||||
|
||||
## Implementation
|
||||
|
||||
### Design Policy
|
||||
* Each pion package should have config object which has `Net` (of type `transport.Net`) property.
|
||||
- Just like how we distribute `LoggerFactory` throughout the pion project.
|
||||
* DNS => a simple dictionary (global)?
|
||||
* Each Net has routing capability (a goroutine)
|
||||
* Use interface provided net package as much as possible
|
||||
* Routers are connected in a tree structure (no loop is allowed)
|
||||
- To simplify routing
|
||||
- Easy to control / monitor (stats, etc)
|
||||
* Root router has no NAT (== Internet / WAN)
|
||||
* Non-root router has a NAT always
|
||||
* When a Net is instantiated, it will automatically add `lo0` and `eth0` interface, and `lo0` will have one IP address, 127.0.0.1. (this is not used in pion/ice, however)
|
||||
* When a Net is added to a router, the router automatically assign an IP address for `eth0` interface.
|
||||
- For simplicity
|
||||
* User data won't fragment, but optionally drop chunk larger than MTU
|
||||
* IPv6 is not supported
|
||||
|
||||
### Basic steps for setting up virtual network
|
||||
1. Create a root router (WAN)
|
||||
1. Create child routers and add to its parent (forms a tree, don't create a loop!)
|
||||
1. Add instances of Net to each routers
|
||||
1. Call Stop(), or Stop(), on the top router, which propagates all other routers
|
||||
|
||||
#### Example: WAN with one endpoint (vnet)
|
||||
```go
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/pion/transport"
|
||||
"github.com/pion/transport/vnet"
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
// Create WAN (a root router).
|
||||
wan, err := vnet.NewRouter(&RouterConfig{
|
||||
CIDR: "0.0.0.0/0",
|
||||
LoggerFactory: logging.NewDefaultLoggerFactory(),
|
||||
})
|
||||
|
||||
// Create a network.
|
||||
// You can specify a static IP for the instance of Net to use. If not specified,
|
||||
// router will assign an IP address that is contained in the router's CIDR.
|
||||
nw := vnet.NewNet(&vnet.NetConfig{
|
||||
StaticIP: "27.1.2.3",
|
||||
})
|
||||
|
||||
// Add the network to the router.
|
||||
// The router will assign an IP address to `nw`.
|
||||
if err = wan.AddNet(nw); err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
// Start router.
|
||||
// This will start internal goroutine to route packets.
|
||||
// If you set child routers (using AddRouter), the call on the root router
|
||||
// will start the rest of routers for you.
|
||||
if err = wan.Start(); err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
//
|
||||
// Your application runs here using `nw`.
|
||||
//
|
||||
|
||||
// Stop the router.
|
||||
// This will stop all internal Go routines in the router tree.
|
||||
// (No need to call Stop() on child routers)
|
||||
if err = wan.Stop(); err != nil {
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
#### Example of how to pass around the instance of vnet.Net
|
||||
The instance of vnet.Net wraps a subset of net package to enable operations
|
||||
on the virtual network. Your project must be able to pass the instance to
|
||||
all your routines that do network operation with net package. A typical way
|
||||
is to use a config param to create your instances with the virtual network
|
||||
instance (`nw` in the above example) like this:
|
||||
|
||||
```go
|
||||
type AgentConfig struct {
|
||||
:
|
||||
Net: transport.Net,
|
||||
}
|
||||
|
||||
type Agent struct {
|
||||
:
|
||||
net: transport.Net,
|
||||
}
|
||||
|
||||
func NetAgent(config *AgentConfig) *Agent {
|
||||
if config.Net == nil {
|
||||
config.Net = vnet.NewNet()
|
||||
}
|
||||
|
||||
return &Agent {
|
||||
:
|
||||
net: config.Net,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// a.net is the instance of vnet.Net class
|
||||
func (a *Agent) listenUDP(...) error {
|
||||
conn, err := a.net.ListenPacket(udpString, ...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
:
|
||||
}
|
||||
```
|
||||
|
||||
### Compatibility and Support Status
|
||||
|
||||
|`net`<br>(built-in) |`vnet` |Note |
|
||||
|:--- |:--- |:--- |
|
||||
| net.Interfaces() | a.net.Interfaces() | |
|
||||
| net.InterfaceByName() | a.net.InterfaceByName() | |
|
||||
| net.ResolveUDPAddr() | a.net.ResolveUDPAddr() | |
|
||||
| net.ListenPacket() | a.net.ListenPacket() | |
|
||||
| net.ListenUDP() | a.net.ListenUDP() | ListenPacket() is recommended |
|
||||
| net.Listen() | a.net.Listen() | TODO) |
|
||||
| net.ListenTCP() | (not supported) | Listen() would be recommended |
|
||||
| net.Dial() | a.net.Dial() | |
|
||||
| net.DialUDP() | a.net.DialUDP() | |
|
||||
| net.DialTCP() | (not supported) | |
|
||||
| net.Interface | transport.Interface | |
|
||||
| net.PacketConn | (use it as-is) | |
|
||||
| net.UDPConn | transport.UDPConn | |
|
||||
| net.TCPConn | transport.TCPConn | TODO: Use net.Conn in your code |
|
||||
| net.Dialer | transport.Dialer | Use a.net.CreateDialer() to create it.<br>The use of vnet.Dialer is currently experimental. |
|
||||
|
||||
> `a.net` is an instance of Net class, and types are defined under the package name `vnet`
|
||||
|
||||
> Most of other `interface` types in net package can be used as is.
|
||||
|
||||
> Please post a github issue when other types/methods need to be added to vnet/vnet.Net.
|
||||
|
||||
## TODO / Next Step
|
||||
* Implement TCP (TCPConn, Listen)
|
||||
* Support of IPv6
|
||||
* Write a bunch of examples for building virtual networks.
|
||||
* Add network impairment features (on Router)
|
||||
- Introduce latency / jitter
|
||||
- Packet filtering handler (allow selectively drop packets, etc.)
|
||||
* Add statistics data retrieval
|
||||
- Total number of packets forward by each router
|
||||
- Total number of packet loss
|
||||
- Total number of connection failure (TCP)
|
||||
|
||||
## References
|
||||
* [Comparing Simulated Packet Loss and RealWorld Network Congestion](https://www.riverbed.com/document/fpo/WhitePaper-Riverbed-SimulatedPacketLoss.pdf)
|
||||
* [wireguard-go using GVisor's netstack](https://github.com/WireGuard/wireguard-go/tree/master/tun/netstack)
|
||||
286
server/vendor/github.com/pion/transport/v2/vnet/chunk.go
generated
vendored
Normal file
286
server/vendor/github.com/pion/transport/v2/vnet/chunk.go
generated
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tcpFlag uint8
|
||||
|
||||
const (
|
||||
tcpFIN tcpFlag = 0x01
|
||||
tcpSYN tcpFlag = 0x02
|
||||
tcpRST tcpFlag = 0x04
|
||||
tcpPSH tcpFlag = 0x08
|
||||
tcpACK tcpFlag = 0x10
|
||||
)
|
||||
|
||||
func (f tcpFlag) String() string {
|
||||
var sa []string
|
||||
if f&tcpFIN != 0 {
|
||||
sa = append(sa, "FIN")
|
||||
}
|
||||
if f&tcpSYN != 0 {
|
||||
sa = append(sa, "SYN")
|
||||
}
|
||||
if f&tcpRST != 0 {
|
||||
sa = append(sa, "RST")
|
||||
}
|
||||
if f&tcpPSH != 0 {
|
||||
sa = append(sa, "PSH")
|
||||
}
|
||||
if f&tcpACK != 0 {
|
||||
sa = append(sa, "ACK")
|
||||
}
|
||||
|
||||
return strings.Join(sa, "-")
|
||||
}
|
||||
|
||||
// Generate a base36-encoded unique tag
|
||||
// See: https://play.golang.org/p/0ZaAID1q-HN
|
||||
var assignChunkTag = func() func() string { //nolint:gochecknoglobals
|
||||
var tagCtr uint64
|
||||
|
||||
return func() string {
|
||||
n := atomic.AddUint64(&tagCtr, 1)
|
||||
return strconv.FormatUint(n, 36)
|
||||
}
|
||||
}()
|
||||
|
||||
// Chunk represents a packet passed around in the vnet
|
||||
type Chunk interface {
|
||||
setTimestamp() time.Time // used by router
|
||||
getTimestamp() time.Time // used by router
|
||||
getSourceIP() net.IP // used by router
|
||||
getDestinationIP() net.IP // used by router
|
||||
setSourceAddr(address string) error // used by nat
|
||||
setDestinationAddr(address string) error // used by nat
|
||||
|
||||
SourceAddr() net.Addr
|
||||
DestinationAddr() net.Addr
|
||||
UserData() []byte
|
||||
Tag() string
|
||||
Clone() Chunk
|
||||
Network() string // returns "udp" or "tcp"
|
||||
String() string
|
||||
}
|
||||
|
||||
type chunkIP struct {
|
||||
timestamp time.Time
|
||||
sourceIP net.IP
|
||||
destinationIP net.IP
|
||||
tag string
|
||||
}
|
||||
|
||||
func (c *chunkIP) setTimestamp() time.Time {
|
||||
c.timestamp = time.Now()
|
||||
return c.timestamp
|
||||
}
|
||||
|
||||
func (c *chunkIP) getTimestamp() time.Time {
|
||||
return c.timestamp
|
||||
}
|
||||
|
||||
func (c *chunkIP) getDestinationIP() net.IP {
|
||||
return c.destinationIP
|
||||
}
|
||||
|
||||
func (c *chunkIP) getSourceIP() net.IP {
|
||||
return c.sourceIP
|
||||
}
|
||||
|
||||
func (c *chunkIP) Tag() string {
|
||||
return c.tag
|
||||
}
|
||||
|
||||
type chunkUDP struct {
|
||||
chunkIP
|
||||
sourcePort int
|
||||
destinationPort int
|
||||
userData []byte
|
||||
}
|
||||
|
||||
func newChunkUDP(srcAddr, dstAddr *net.UDPAddr) *chunkUDP {
|
||||
return &chunkUDP{
|
||||
chunkIP: chunkIP{
|
||||
sourceIP: srcAddr.IP,
|
||||
destinationIP: dstAddr.IP,
|
||||
tag: assignChunkTag(),
|
||||
},
|
||||
sourcePort: srcAddr.Port,
|
||||
destinationPort: dstAddr.Port,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkUDP) SourceAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: c.sourceIP,
|
||||
Port: c.sourcePort,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkUDP) DestinationAddr() net.Addr {
|
||||
return &net.UDPAddr{
|
||||
IP: c.destinationIP,
|
||||
Port: c.destinationPort,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkUDP) UserData() []byte {
|
||||
return c.userData
|
||||
}
|
||||
|
||||
func (c *chunkUDP) Clone() Chunk {
|
||||
var userData []byte
|
||||
if c.userData != nil {
|
||||
userData = make([]byte, len(c.userData))
|
||||
copy(userData, c.userData)
|
||||
}
|
||||
|
||||
return &chunkUDP{
|
||||
chunkIP: chunkIP{
|
||||
timestamp: c.timestamp,
|
||||
sourceIP: c.sourceIP,
|
||||
destinationIP: c.destinationIP,
|
||||
tag: c.tag,
|
||||
},
|
||||
sourcePort: c.sourcePort,
|
||||
destinationPort: c.destinationPort,
|
||||
userData: userData,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkUDP) Network() string {
|
||||
return udp
|
||||
}
|
||||
|
||||
func (c *chunkUDP) String() string {
|
||||
src := c.SourceAddr()
|
||||
dst := c.DestinationAddr()
|
||||
return fmt.Sprintf("%s chunk %s %s => %s",
|
||||
src.Network(),
|
||||
c.tag,
|
||||
src.String(),
|
||||
dst.String(),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *chunkUDP) setSourceAddr(address string) error {
|
||||
addr, err := net.ResolveUDPAddr(udp, address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.sourceIP = addr.IP
|
||||
c.sourcePort = addr.Port
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkUDP) setDestinationAddr(address string) error {
|
||||
addr, err := net.ResolveUDPAddr(udp, address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.destinationIP = addr.IP
|
||||
c.destinationPort = addr.Port
|
||||
return nil
|
||||
}
|
||||
|
||||
type chunkTCP struct {
|
||||
chunkIP
|
||||
sourcePort int
|
||||
destinationPort int
|
||||
flags tcpFlag // control bits
|
||||
userData []byte // only with PSH flag
|
||||
// seq uint32 // always starts with 0
|
||||
// ack uint32 // always starts with 0
|
||||
}
|
||||
|
||||
func newChunkTCP(srcAddr, dstAddr *net.TCPAddr, flags tcpFlag) *chunkTCP {
|
||||
return &chunkTCP{
|
||||
chunkIP: chunkIP{
|
||||
sourceIP: srcAddr.IP,
|
||||
destinationIP: dstAddr.IP,
|
||||
tag: assignChunkTag(),
|
||||
},
|
||||
sourcePort: srcAddr.Port,
|
||||
destinationPort: dstAddr.Port,
|
||||
flags: flags,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkTCP) SourceAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: c.sourceIP,
|
||||
Port: c.sourcePort,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkTCP) DestinationAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: c.destinationIP,
|
||||
Port: c.destinationPort,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkTCP) UserData() []byte {
|
||||
return c.userData
|
||||
}
|
||||
|
||||
func (c *chunkTCP) Clone() Chunk {
|
||||
userData := make([]byte, len(c.userData))
|
||||
copy(userData, c.userData)
|
||||
|
||||
return &chunkTCP{
|
||||
chunkIP: chunkIP{
|
||||
timestamp: c.timestamp,
|
||||
sourceIP: c.sourceIP,
|
||||
destinationIP: c.destinationIP,
|
||||
},
|
||||
sourcePort: c.sourcePort,
|
||||
destinationPort: c.destinationPort,
|
||||
userData: userData,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *chunkTCP) Network() string {
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
func (c *chunkTCP) String() string {
|
||||
src := c.SourceAddr()
|
||||
dst := c.DestinationAddr()
|
||||
return fmt.Sprintf("%s %s chunk %s %s => %s",
|
||||
src.Network(),
|
||||
c.flags.String(),
|
||||
c.tag,
|
||||
src.String(),
|
||||
dst.String(),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *chunkTCP) setSourceAddr(address string) error {
|
||||
addr, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.sourceIP = addr.IP
|
||||
c.sourcePort = addr.Port
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *chunkTCP) setDestinationAddr(address string) error {
|
||||
addr, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.destinationIP = addr.IP
|
||||
c.destinationPort = addr.Port
|
||||
return nil
|
||||
}
|
||||
68
server/vendor/github.com/pion/transport/v2/vnet/chunk_queue.go
generated
vendored
Normal file
68
server/vendor/github.com/pion/transport/v2/vnet/chunk_queue.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type chunkQueue struct {
|
||||
chunks []Chunk
|
||||
maxSize int // 0 or negative value: unlimited
|
||||
maxBytes int // 0 or negative value: unlimited
|
||||
currentBytes int
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func newChunkQueue(maxSize int, maxBytes int) *chunkQueue {
|
||||
return &chunkQueue{
|
||||
chunks: []Chunk{},
|
||||
maxSize: maxSize,
|
||||
maxBytes: maxBytes,
|
||||
currentBytes: 0,
|
||||
mutex: sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (q *chunkQueue) push(c Chunk) bool {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
if q.maxSize > 0 && len(q.chunks) >= q.maxSize {
|
||||
return false // dropped
|
||||
}
|
||||
if q.maxBytes > 0 && q.currentBytes+len(c.UserData()) >= q.maxBytes {
|
||||
return false
|
||||
}
|
||||
|
||||
q.currentBytes += len(c.UserData())
|
||||
q.chunks = append(q.chunks, c)
|
||||
return true
|
||||
}
|
||||
|
||||
func (q *chunkQueue) pop() (Chunk, bool) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
if len(q.chunks) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
c := q.chunks[0]
|
||||
q.chunks = q.chunks[1:]
|
||||
q.currentBytes -= len(c.UserData())
|
||||
|
||||
return c, true
|
||||
}
|
||||
|
||||
func (q *chunkQueue) peek() Chunk {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
if len(q.chunks) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return q.chunks[0]
|
||||
}
|
||||
298
server/vendor/github.com/pion/transport/v2/vnet/conn.go
generated
vendored
Normal file
298
server/vendor/github.com/pion/transport/v2/vnet/conn.go
generated
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
maxReadQueueSize = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
errObsCannotBeNil = errors.New("obs cannot be nil")
|
||||
errUseClosedNetworkConn = errors.New("use of closed network connection")
|
||||
errAddrNotUDPAddr = errors.New("addr is not a net.UDPAddr")
|
||||
errLocAddr = errors.New("something went wrong with locAddr")
|
||||
errAlreadyClosed = errors.New("already closed")
|
||||
errNoRemAddr = errors.New("no remAddr defined")
|
||||
)
|
||||
|
||||
// vNet implements this
|
||||
type connObserver interface {
|
||||
write(c Chunk) error
|
||||
onClosed(addr net.Addr)
|
||||
determineSourceIP(locIP, dstIP net.IP) net.IP
|
||||
}
|
||||
|
||||
// UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections.
|
||||
// compatible with net.PacketConn and net.Conn
|
||||
type UDPConn struct {
|
||||
locAddr *net.UDPAddr // read-only
|
||||
remAddr *net.UDPAddr // read-only
|
||||
obs connObserver // read-only
|
||||
readCh chan Chunk // thread-safe
|
||||
closed bool // requires mutex
|
||||
mu sync.Mutex // to mutex closed flag
|
||||
readTimer *time.Timer // thread-safe
|
||||
}
|
||||
|
||||
var _ transport.UDPConn = &UDPConn{}
|
||||
|
||||
func newUDPConn(locAddr, remAddr *net.UDPAddr, obs connObserver) (*UDPConn, error) {
|
||||
if obs == nil {
|
||||
return nil, errObsCannotBeNil
|
||||
}
|
||||
|
||||
return &UDPConn{
|
||||
locAddr: locAddr,
|
||||
remAddr: remAddr,
|
||||
obs: obs,
|
||||
readCh: make(chan Chunk, maxReadQueueSize),
|
||||
readTimer: time.NewTimer(time.Duration(math.MaxInt64)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
|
||||
func (c *UDPConn) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return errAlreadyClosed
|
||||
}
|
||||
c.closed = true
|
||||
close(c.readCh)
|
||||
|
||||
c.obs.onClosed(c.locAddr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (c *UDPConn) LocalAddr() net.Addr {
|
||||
return c.locAddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (c *UDPConn) RemoteAddr() net.Addr {
|
||||
return c.remAddr
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection. It is equivalent to calling both
|
||||
// SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations
|
||||
// fail with a timeout (see type Error) instead of
|
||||
// blocking. The deadline applies to all future and pending
|
||||
// I/O, not just the immediately following call to ReadFrom or
|
||||
// WriteTo. After a deadline has been exceeded, the connection
|
||||
// can be refreshed by setting a deadline in the future.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful ReadFrom or WriteTo calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
func (c *UDPConn) SetDeadline(t time.Time) error {
|
||||
return c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future ReadFrom calls
|
||||
// and any currently-blocked ReadFrom call.
|
||||
// A zero value for t means ReadFrom will not time out.
|
||||
func (c *UDPConn) SetReadDeadline(t time.Time) error {
|
||||
var d time.Duration
|
||||
var noDeadline time.Time
|
||||
if t == noDeadline {
|
||||
d = time.Duration(math.MaxInt64)
|
||||
} else {
|
||||
d = time.Until(t)
|
||||
}
|
||||
c.readTimer.Reset(d)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future WriteTo calls
|
||||
// and any currently-blocked WriteTo call.
|
||||
// Even if write times out, it may return n > 0, indicating that
|
||||
// some of the data was successfully written.
|
||||
// A zero value for t means WriteTo will not time out.
|
||||
func (c *UDPConn) SetWriteDeadline(time.Time) error {
|
||||
// Write never blocks.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads data from the connection.
|
||||
// Read can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetReadDeadline.
|
||||
func (c *UDPConn) Read(b []byte) (int, error) {
|
||||
n, _, err := c.ReadFrom(b)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadFrom reads a packet from the connection,
|
||||
// copying the payload into p. It returns the number of
|
||||
// bytes copied into p and the return address that
|
||||
// was on the packet.
|
||||
// It returns the number of bytes read (0 <= n <= len(p))
|
||||
// and any error encountered. Callers should always process
|
||||
// the n > 0 bytes returned before considering the error err.
|
||||
// ReadFrom can be made to time out and return
|
||||
// an Error with Timeout() == true after a fixed time limit;
|
||||
// see SetDeadline and SetReadDeadline.
|
||||
func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case chunk, ok := <-c.readCh:
|
||||
if !ok {
|
||||
break loop
|
||||
}
|
||||
var err error
|
||||
n := copy(p, chunk.UserData())
|
||||
addr := chunk.SourceAddr()
|
||||
if n < len(chunk.UserData()) {
|
||||
err = io.ErrShortBuffer
|
||||
}
|
||||
|
||||
if c.remAddr != nil {
|
||||
if addr.String() != c.remAddr.String() {
|
||||
break // discard (shouldn't happen)
|
||||
}
|
||||
}
|
||||
return n, addr, err
|
||||
|
||||
case <-c.readTimer.C:
|
||||
return 0, nil, &net.OpError{
|
||||
Op: "read",
|
||||
Net: c.locAddr.Network(),
|
||||
Addr: c.locAddr,
|
||||
Err: newTimeoutError("i/o timeout"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil, &net.OpError{
|
||||
Op: "read",
|
||||
Net: c.locAddr.Network(),
|
||||
Addr: c.locAddr,
|
||||
Err: errUseClosedNetworkConn,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFromUDP acts like ReadFrom but returns a UDPAddr.
|
||||
func (c *UDPConn) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) {
|
||||
n, addr, err := c.ReadFrom(b)
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return -1, nil, fmt.Errorf("%w: %s", transport.ErrNotUDPAddress, addr)
|
||||
}
|
||||
|
||||
return n, udpAddr, err
|
||||
}
|
||||
|
||||
// ReadMsgUDP reads a message from c, copying the payload into b and
|
||||
// the associated out-of-band data into oob. It returns the number of
|
||||
// bytes copied into b, the number of bytes copied into oob, the flags
|
||||
// that were set on the message and the source address of the message.
|
||||
//
|
||||
// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be
|
||||
// used to manipulate IP-level socket options in oob.
|
||||
func (c *UDPConn) ReadMsgUDP([]byte, []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
|
||||
return -1, -1, -1, nil, transport.ErrNotSupported
|
||||
}
|
||||
|
||||
// Write writes data to the connection.
|
||||
// Write can be made to time out and return an Error with Timeout() == true
|
||||
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
|
||||
func (c *UDPConn) Write(b []byte) (int, error) {
|
||||
if c.remAddr == nil {
|
||||
return 0, errNoRemAddr
|
||||
}
|
||||
|
||||
return c.WriteTo(b, c.remAddr)
|
||||
}
|
||||
|
||||
// WriteTo writes a packet with payload p to addr.
|
||||
// WriteTo can be made to time out and return
|
||||
// an Error with Timeout() == true after a fixed time limit;
|
||||
// see SetDeadline and SetWriteDeadline.
|
||||
// On packet-oriented connections, write timeouts are rare.
|
||||
func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
dstAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, errAddrNotUDPAddr
|
||||
}
|
||||
|
||||
srcIP := c.obs.determineSourceIP(c.locAddr.IP, dstAddr.IP)
|
||||
if srcIP == nil {
|
||||
return 0, errLocAddr
|
||||
}
|
||||
srcAddr := &net.UDPAddr{
|
||||
IP: srcIP,
|
||||
Port: c.locAddr.Port,
|
||||
}
|
||||
|
||||
chunk := newChunkUDP(srcAddr, dstAddr)
|
||||
chunk.userData = make([]byte, len(p))
|
||||
copy(chunk.userData, p)
|
||||
if err := c.obs.write(chunk); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteToUDP acts like WriteTo but takes a UDPAddr.
|
||||
func (c *UDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
||||
return c.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
// WriteMsgUDP writes a message to addr via c if c isn't connected, or
|
||||
// to c's remote address if c is connected (in which case addr must be
|
||||
// nil). The payload is copied from b and the associated out-of-band
|
||||
// data is copied from oob. It returns the number of payload and
|
||||
// out-of-band bytes written.
|
||||
//
|
||||
// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be
|
||||
// used to manipulate IP-level socket options in oob.
|
||||
func (c *UDPConn) WriteMsgUDP([]byte, []byte, *net.UDPAddr) (n, oobn int, err error) {
|
||||
return -1, -1, transport.ErrNotSupported
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's
|
||||
// receive buffer associated with the connection.
|
||||
func (c *UDPConn) SetReadBuffer(int) error {
|
||||
return transport.ErrNotSupported
|
||||
}
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's
|
||||
// transmit buffer associated with the connection.
|
||||
func (c *UDPConn) SetWriteBuffer(int) error {
|
||||
return transport.ErrNotSupported
|
||||
}
|
||||
|
||||
func (c *UDPConn) onInboundChunk(chunk Chunk) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.closed {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case c.readCh <- chunk:
|
||||
default:
|
||||
}
|
||||
}
|
||||
139
server/vendor/github.com/pion/transport/v2/vnet/conn_map.go
generated
vendored
Normal file
139
server/vendor/github.com/pion/transport/v2/vnet/conn_map.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
errAddressAlreadyInUse = errors.New("address already in use")
|
||||
errNoSuchUDPConn = errors.New("no such UDPConn")
|
||||
errCannotRemoveUnspecifiedIP = errors.New("cannot remove unspecified IP by the specified IP")
|
||||
)
|
||||
|
||||
type udpConnMap struct {
|
||||
portMap map[int][]*UDPConn
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func newUDPConnMap() *udpConnMap {
|
||||
return &udpConnMap{
|
||||
portMap: map[int][]*UDPConn{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *udpConnMap) insert(conn *UDPConn) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
udpAddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
|
||||
// check if the port has a listener
|
||||
conns, ok := m.portMap[udpAddr.Port]
|
||||
if ok {
|
||||
if udpAddr.IP.IsUnspecified() {
|
||||
return errAddressAlreadyInUse
|
||||
}
|
||||
|
||||
for _, conn := range conns {
|
||||
laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
|
||||
return errAddressAlreadyInUse
|
||||
}
|
||||
}
|
||||
|
||||
conns = append(conns, conn)
|
||||
} else {
|
||||
conns = []*UDPConn{conn}
|
||||
}
|
||||
|
||||
m.portMap[udpAddr.Port] = conns
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *udpConnMap) find(addr net.Addr) (*UDPConn, bool) {
|
||||
m.mutex.Lock() // could be RLock, but we have delete() op
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
udpAddr := addr.(*net.UDPAddr) //nolint:forcetypeassert
|
||||
|
||||
if conns, ok := m.portMap[udpAddr.Port]; ok {
|
||||
if udpAddr.IP.IsUnspecified() {
|
||||
// pick the first one appears in the iteration
|
||||
if len(conns) == 0 {
|
||||
// This can't happen!
|
||||
delete(m.portMap, udpAddr.Port)
|
||||
return nil, false
|
||||
}
|
||||
return conns[0], true
|
||||
}
|
||||
|
||||
for _, conn := range conns {
|
||||
laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
|
||||
return conn, ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *udpConnMap) delete(addr net.Addr) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
udpAddr := addr.(*net.UDPAddr) //nolint:forcetypeassert
|
||||
|
||||
conns, ok := m.portMap[udpAddr.Port]
|
||||
if !ok {
|
||||
return errNoSuchUDPConn
|
||||
}
|
||||
|
||||
if udpAddr.IP.IsUnspecified() {
|
||||
// remove all from this port
|
||||
delete(m.portMap, udpAddr.Port)
|
||||
return nil
|
||||
}
|
||||
|
||||
newConns := []*UDPConn{}
|
||||
|
||||
for _, conn := range conns {
|
||||
laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
if laddr.IP.IsUnspecified() {
|
||||
// This can't happen!
|
||||
return errCannotRemoveUnspecifiedIP
|
||||
}
|
||||
|
||||
if laddr.IP.Equal(udpAddr.IP) {
|
||||
continue
|
||||
}
|
||||
|
||||
newConns = append(newConns, conn)
|
||||
}
|
||||
|
||||
if len(newConns) == 0 {
|
||||
delete(m.portMap, udpAddr.Port)
|
||||
} else {
|
||||
m.portMap[udpAddr.Port] = newConns
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// size returns the number of UDPConns (UDP listeners)
|
||||
func (m *udpConnMap) size() int {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
n := 0
|
||||
for _, conns := range m.portMap {
|
||||
n += len(conns)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
78
server/vendor/github.com/pion/transport/v2/vnet/delay_filter.go
generated
vendored
Normal file
78
server/vendor/github.com/pion/transport/v2/vnet/delay_filter.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DelayFilter delays outgoing packets by the given delay. Run must be called
|
||||
// before any packets will be forwarded.
|
||||
type DelayFilter struct {
|
||||
NIC
|
||||
delay time.Duration
|
||||
push chan struct{}
|
||||
queue *chunkQueue
|
||||
}
|
||||
|
||||
type timedChunk struct {
|
||||
Chunk
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
// NewDelayFilter creates a new DelayFilter with the given nic and delay.
|
||||
func NewDelayFilter(nic NIC, delay time.Duration) (*DelayFilter, error) {
|
||||
return &DelayFilter{
|
||||
NIC: nic,
|
||||
delay: delay,
|
||||
push: make(chan struct{}),
|
||||
queue: newChunkQueue(0, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *DelayFilter) onInboundChunk(c Chunk) {
|
||||
f.queue.push(timedChunk{
|
||||
Chunk: c,
|
||||
deadline: time.Now().Add(f.delay),
|
||||
})
|
||||
f.push <- struct{}{}
|
||||
}
|
||||
|
||||
// Run starts forwarding of packets. Packets will be forwarded if they spent
|
||||
// >delay time in the internal queue. Must be called before any packet will be
|
||||
// forwarded.
|
||||
func (f *DelayFilter) Run(ctx context.Context) {
|
||||
timer := time.NewTimer(0)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-f.push:
|
||||
next := f.queue.peek().(timedChunk) //nolint:forcetypeassert
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
timer.Reset(time.Until(next.deadline))
|
||||
case now := <-timer.C:
|
||||
next := f.queue.peek()
|
||||
if next == nil {
|
||||
timer.Reset(time.Minute)
|
||||
continue
|
||||
}
|
||||
if n, ok := next.(timedChunk); ok && n.deadline.Before(now) {
|
||||
f.queue.pop() // ignore result because we already got and casted it from peek
|
||||
f.NIC.onInboundChunk(n.Chunk)
|
||||
}
|
||||
next = f.queue.peek()
|
||||
if next == nil {
|
||||
timer.Reset(time.Minute)
|
||||
continue
|
||||
}
|
||||
if n, ok := next.(timedChunk); ok {
|
||||
timer.Reset(time.Until(n.deadline))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
server/vendor/github.com/pion/transport/v2/vnet/errors.go
generated
vendored
Normal file
22
server/vendor/github.com/pion/transport/v2/vnet/errors.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
type timeoutError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func newTimeoutError(msg string) error {
|
||||
return &timeoutError{
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *timeoutError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e *timeoutError) Timeout() bool {
|
||||
return true
|
||||
}
|
||||
36
server/vendor/github.com/pion/transport/v2/vnet/loss_filter.go
generated
vendored
Normal file
36
server/vendor/github.com/pion/transport/v2/vnet/loss_filter.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LossFilter is a wrapper around NICs, that drops some of the packets passed to
|
||||
// onInboundChunk
|
||||
type LossFilter struct {
|
||||
NIC
|
||||
chance int
|
||||
}
|
||||
|
||||
// NewLossFilter creates a new LossFilter that drops every packet with a
|
||||
// probability of chance/100. Every packet that is not dropped is passed on to
|
||||
// the given NIC.
|
||||
func NewLossFilter(nic NIC, chance int) (*LossFilter, error) {
|
||||
f := &LossFilter{
|
||||
NIC: nic,
|
||||
chance: chance,
|
||||
}
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *LossFilter) onInboundChunk(c Chunk) {
|
||||
if rand.Intn(100) < f.chance { //nolint:gosec
|
||||
return
|
||||
}
|
||||
|
||||
f.NIC.onInboundChunk(c)
|
||||
}
|
||||
342
server/vendor/github.com/pion/transport/v2/vnet/nat.go
generated
vendored
Normal file
342
server/vendor/github.com/pion/transport/v2/vnet/nat.go
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
errNATRequriesMapping = errors.New("1:1 NAT requires more than one mapping")
|
||||
errMismatchLengthIP = errors.New("length mismtach between mappedIPs and localIPs")
|
||||
errNonUDPTranslationNotSupported = errors.New("non-udp translation is not supported yet")
|
||||
errNoAssociatedLocalAddress = errors.New("no associated local address")
|
||||
errNoNATBindingFound = errors.New("no NAT binding found")
|
||||
errHasNoPermission = errors.New("has no permission")
|
||||
)
|
||||
|
||||
// EndpointDependencyType defines a type of behavioral dependendency on the
|
||||
// remote endpoint's IP address or port number. This is used for the two
|
||||
// kinds of behaviors:
|
||||
// - Port mapping behavior
|
||||
// - Filtering behavior
|
||||
//
|
||||
// See: https://tools.ietf.org/html/rfc4787
|
||||
type EndpointDependencyType uint8
|
||||
|
||||
const (
|
||||
// EndpointIndependent means the behavior is independent of the endpoint's address or port
|
||||
EndpointIndependent EndpointDependencyType = iota
|
||||
// EndpointAddrDependent means the behavior is dependent on the endpoint's address
|
||||
EndpointAddrDependent
|
||||
// EndpointAddrPortDependent means the behavior is dependent on the endpoint's address and port
|
||||
EndpointAddrPortDependent
|
||||
)
|
||||
|
||||
// NATMode defines basic behavior of the NAT
|
||||
type NATMode uint8
|
||||
|
||||
const (
|
||||
// NATModeNormal means the NAT behaves as a standard NAPT (RFC 2663).
|
||||
NATModeNormal NATMode = iota
|
||||
// NATModeNAT1To1 exhibits 1:1 DNAT where the external IP address is statically mapped to
|
||||
// a specific local IP address with port number is preserved always between them.
|
||||
// When this mode is selected, MappingBehavior, FilteringBehavior, PortPreservation and
|
||||
// MappingLifeTime of NATType are ignored.
|
||||
NATModeNAT1To1
|
||||
)
|
||||
|
||||
const (
|
||||
defaultNATMappingLifeTime = 30 * time.Second
|
||||
)
|
||||
|
||||
// NATType has a set of parameters that define the behavior of NAT.
|
||||
type NATType struct {
|
||||
Mode NATMode
|
||||
MappingBehavior EndpointDependencyType
|
||||
FilteringBehavior EndpointDependencyType
|
||||
Hairpinning bool // Not implemented yet
|
||||
PortPreservation bool // Not implemented yet
|
||||
MappingLifeTime time.Duration
|
||||
}
|
||||
|
||||
type natConfig struct {
|
||||
name string
|
||||
natType NATType
|
||||
mappedIPs []net.IP // mapped IPv4
|
||||
localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
|
||||
loggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
type mapping struct {
|
||||
proto string // "udp" or "tcp"
|
||||
local string // "<local-ip>:<local-port>"
|
||||
mapped string // "<mapped-ip>:<mapped-port>"
|
||||
bound string // key: "[<remote-ip>[:<remote-port>]]"
|
||||
filters map[string]struct{} // key: "[<remote-ip>[:<remote-port>]]"
|
||||
expires time.Time // time to expire
|
||||
}
|
||||
|
||||
type networkAddressTranslator struct {
|
||||
name string
|
||||
natType NATType
|
||||
mappedIPs []net.IP // mapped IPv4
|
||||
localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
|
||||
outboundMap map[string]*mapping // key: "<proto>:<local-ip>:<local-port>[:remote-ip[:remote-port]]
|
||||
inboundMap map[string]*mapping // key: "<proto>:<mapped-ip>:<mapped-port>"
|
||||
udpPortCounter int
|
||||
mutex sync.RWMutex
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
func newNAT(config *natConfig) (*networkAddressTranslator, error) {
|
||||
natType := config.natType
|
||||
|
||||
if natType.Mode == NATModeNAT1To1 {
|
||||
// 1:1 NAT behavior
|
||||
natType.MappingBehavior = EndpointIndependent
|
||||
natType.FilteringBehavior = EndpointIndependent
|
||||
natType.PortPreservation = true
|
||||
natType.MappingLifeTime = 0
|
||||
|
||||
if len(config.mappedIPs) == 0 {
|
||||
return nil, errNATRequriesMapping
|
||||
}
|
||||
if len(config.mappedIPs) != len(config.localIPs) {
|
||||
return nil, errMismatchLengthIP
|
||||
}
|
||||
} else {
|
||||
// Normal (NAPT) behavior
|
||||
natType.Mode = NATModeNormal
|
||||
if natType.MappingLifeTime == 0 {
|
||||
natType.MappingLifeTime = defaultNATMappingLifeTime
|
||||
}
|
||||
}
|
||||
|
||||
return &networkAddressTranslator{
|
||||
name: config.name,
|
||||
natType: natType,
|
||||
mappedIPs: config.mappedIPs,
|
||||
localIPs: config.localIPs,
|
||||
outboundMap: map[string]*mapping{},
|
||||
inboundMap: map[string]*mapping{},
|
||||
log: config.loggerFactory.NewLogger("vnet"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *networkAddressTranslator) getPairedMappedIP(locIP net.IP) net.IP {
|
||||
for i, ip := range n.localIPs {
|
||||
if ip.Equal(locIP) {
|
||||
return n.mappedIPs[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *networkAddressTranslator) getPairedLocalIP(mappedIP net.IP) net.IP {
|
||||
for i, ip := range n.mappedIPs {
|
||||
if ip.Equal(mappedIP) {
|
||||
return n.localIPs[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *networkAddressTranslator) translateOutbound(from Chunk) (Chunk, error) {
|
||||
n.mutex.Lock()
|
||||
defer n.mutex.Unlock()
|
||||
|
||||
to := from.Clone()
|
||||
|
||||
if from.Network() == udp {
|
||||
if n.natType.Mode == NATModeNAT1To1 {
|
||||
// 1:1 NAT behavior
|
||||
srcAddr := from.SourceAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
srcIP := n.getPairedMappedIP(srcAddr.IP)
|
||||
if srcIP == nil {
|
||||
n.log.Debugf("[%s] drop outbound chunk %s with not route", n.name, from.String())
|
||||
return nil, nil // nolint:nilnil
|
||||
}
|
||||
srcPort := srcAddr.Port
|
||||
if err := to.setSourceAddr(fmt.Sprintf("%s:%d", srcIP.String(), srcPort)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Normal (NAPT) behavior
|
||||
var bound, filterKey string
|
||||
switch n.natType.MappingBehavior {
|
||||
case EndpointIndependent:
|
||||
bound = ""
|
||||
case EndpointAddrDependent:
|
||||
bound = from.getDestinationIP().String()
|
||||
case EndpointAddrPortDependent:
|
||||
bound = from.DestinationAddr().String()
|
||||
}
|
||||
|
||||
switch n.natType.FilteringBehavior {
|
||||
case EndpointIndependent:
|
||||
filterKey = ""
|
||||
case EndpointAddrDependent:
|
||||
filterKey = from.getDestinationIP().String()
|
||||
case EndpointAddrPortDependent:
|
||||
filterKey = from.DestinationAddr().String()
|
||||
}
|
||||
|
||||
oKey := fmt.Sprintf("udp:%s:%s", from.SourceAddr().String(), bound)
|
||||
|
||||
m := n.findOutboundMapping(oKey)
|
||||
if m == nil {
|
||||
// Create a new mapping
|
||||
mappedPort := 0xC000 + n.udpPortCounter
|
||||
n.udpPortCounter++
|
||||
|
||||
m = &mapping{
|
||||
proto: from.SourceAddr().Network(),
|
||||
local: from.SourceAddr().String(),
|
||||
bound: bound,
|
||||
mapped: fmt.Sprintf("%s:%d", n.mappedIPs[0].String(), mappedPort),
|
||||
filters: map[string]struct{}{},
|
||||
expires: time.Now().Add(n.natType.MappingLifeTime),
|
||||
}
|
||||
|
||||
n.outboundMap[oKey] = m
|
||||
|
||||
iKey := fmt.Sprintf("udp:%s", m.mapped)
|
||||
|
||||
n.log.Debugf("[%s] created a new NAT binding oKey=%s iKey=%s",
|
||||
n.name,
|
||||
oKey,
|
||||
iKey)
|
||||
|
||||
m.filters[filterKey] = struct{}{}
|
||||
n.log.Debugf("[%s] permit access from %s to %s", n.name, filterKey, m.mapped)
|
||||
n.inboundMap[iKey] = m
|
||||
} else if _, ok := m.filters[filterKey]; !ok {
|
||||
n.log.Debugf("[%s] permit access from %s to %s", n.name, filterKey, m.mapped)
|
||||
m.filters[filterKey] = struct{}{}
|
||||
}
|
||||
|
||||
if err := to.setSourceAddr(m.mapped); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
n.log.Debugf("[%s] translate outbound chunk from %s to %s", n.name, from.String(), to.String())
|
||||
|
||||
return to, nil
|
||||
}
|
||||
|
||||
return nil, errNonUDPTranslationNotSupported
|
||||
}
|
||||
|
||||
func (n *networkAddressTranslator) translateInbound(from Chunk) (Chunk, error) {
|
||||
n.mutex.Lock()
|
||||
defer n.mutex.Unlock()
|
||||
|
||||
to := from.Clone()
|
||||
|
||||
if from.Network() == udp {
|
||||
if n.natType.Mode == NATModeNAT1To1 {
|
||||
// 1:1 NAT behavior
|
||||
dstAddr := from.DestinationAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
dstIP := n.getPairedLocalIP(dstAddr.IP)
|
||||
if dstIP == nil {
|
||||
return nil, fmt.Errorf("drop %s as %w", from.String(), errNoAssociatedLocalAddress)
|
||||
}
|
||||
dstPort := from.DestinationAddr().(*net.UDPAddr).Port //nolint:forcetypeassert
|
||||
if err := to.setDestinationAddr(fmt.Sprintf("%s:%d", dstIP, dstPort)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Normal (NAPT) behavior
|
||||
iKey := fmt.Sprintf("udp:%s", from.DestinationAddr().String())
|
||||
m := n.findInboundMapping(iKey)
|
||||
if m == nil {
|
||||
return nil, fmt.Errorf("drop %s as %w", from.String(), errNoNATBindingFound)
|
||||
}
|
||||
|
||||
var filterKey string
|
||||
switch n.natType.FilteringBehavior {
|
||||
case EndpointIndependent:
|
||||
filterKey = ""
|
||||
case EndpointAddrDependent:
|
||||
filterKey = from.getSourceIP().String()
|
||||
case EndpointAddrPortDependent:
|
||||
filterKey = from.SourceAddr().String()
|
||||
}
|
||||
|
||||
if _, ok := m.filters[filterKey]; !ok {
|
||||
return nil, fmt.Errorf("drop %s as the remote %s %w", from.String(), filterKey, errHasNoPermission)
|
||||
}
|
||||
|
||||
// See RFC 4847 Section 4.3. Mapping Refresh
|
||||
// a) Inbound refresh may be useful for applications with no outgoing
|
||||
// UDP traffic. However, allowing inbound refresh may allow an
|
||||
// external attacker or misbehaving application to keep a mapping
|
||||
// alive indefinitely. This may be a security risk. Also, if the
|
||||
// process is repeated with different ports, over time, it could
|
||||
// use up all the ports on the NAT.
|
||||
|
||||
if err := to.setDestinationAddr(m.local); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
n.log.Debugf("[%s] translate inbound chunk from %s to %s", n.name, from.String(), to.String())
|
||||
|
||||
return to, nil
|
||||
}
|
||||
|
||||
return nil, errNonUDPTranslationNotSupported
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (n *networkAddressTranslator) findOutboundMapping(oKey string) *mapping {
|
||||
now := time.Now()
|
||||
|
||||
m, ok := n.outboundMap[oKey]
|
||||
if ok {
|
||||
// check if this mapping is expired
|
||||
if now.After(m.expires) {
|
||||
n.removeMapping(m)
|
||||
m = nil // expired
|
||||
} else {
|
||||
m.expires = time.Now().Add(n.natType.MappingLifeTime)
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (n *networkAddressTranslator) findInboundMapping(iKey string) *mapping {
|
||||
now := time.Now()
|
||||
m, ok := n.inboundMap[iKey]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if this mapping is expired
|
||||
if now.After(m.expires) {
|
||||
n.removeMapping(m)
|
||||
return nil
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (n *networkAddressTranslator) removeMapping(m *mapping) {
|
||||
oKey := fmt.Sprintf("%s:%s:%s", m.proto, m.local, m.bound)
|
||||
iKey := fmt.Sprintf("%s:%s", m.proto, m.mapped)
|
||||
|
||||
delete(n.outboundMap, oKey)
|
||||
delete(n.inboundMap, iKey)
|
||||
}
|
||||
618
server/vendor/github.com/pion/transport/v2/vnet/net.go
generated
vendored
Normal file
618
server/vendor/github.com/pion/transport/v2/vnet/net.go
generated
vendored
Normal file
@@ -0,0 +1,618 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
lo0String = "lo0String"
|
||||
udp = "udp"
|
||||
udp4 = "udp4"
|
||||
)
|
||||
|
||||
var (
|
||||
macAddrCounter uint64 = 0xBEEFED910200 //nolint:gochecknoglobals
|
||||
errNoInterface = errors.New("no interface is available")
|
||||
errUnexpectedNetwork = errors.New("unexpected network")
|
||||
errCantAssignRequestedAddr = errors.New("can't assign requested address")
|
||||
errUnknownNetwork = errors.New("unknown network")
|
||||
errNoRouterLinked = errors.New("no router linked")
|
||||
errInvalidPortNumber = errors.New("invalid port number")
|
||||
errUnexpectedTypeSwitchFailure = errors.New("unexpected type-switch failure")
|
||||
errBindFailedFor = errors.New("bind failed for")
|
||||
errEndPortLessThanStart = errors.New("end port is less than the start")
|
||||
errPortSpaceExhausted = errors.New("port space exhausted")
|
||||
)
|
||||
|
||||
func newMACAddress() net.HardwareAddr {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, macAddrCounter)
|
||||
macAddrCounter++
|
||||
return b[2:]
|
||||
}
|
||||
|
||||
// Net represents a local network stack equivalent to a set of layers from NIC
|
||||
// up to the transport (UDP / TCP) layer.
|
||||
type Net struct {
|
||||
interfaces []*transport.Interface // read-only
|
||||
staticIPs []net.IP // read-only
|
||||
router *Router // read-only
|
||||
udpConns *udpConnMap // read-only
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// Compile-time assertion
|
||||
var _ transport.Net = &Net{}
|
||||
|
||||
func (v *Net) _getInterfaces() ([]*transport.Interface, error) {
|
||||
if len(v.interfaces) == 0 {
|
||||
return nil, errNoInterface
|
||||
}
|
||||
|
||||
return v.interfaces, nil
|
||||
}
|
||||
|
||||
// Interfaces returns a list of the system's network interfaces.
|
||||
func (v *Net) Interfaces() ([]*transport.Interface, error) {
|
||||
v.mutex.RLock()
|
||||
defer v.mutex.RUnlock()
|
||||
|
||||
return v._getInterfaces()
|
||||
}
|
||||
|
||||
// caller must hold the mutex (read)
|
||||
func (v *Net) _getInterface(ifName string) (*transport.Interface, error) {
|
||||
ifs, err := v._getInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifc := range ifs {
|
||||
if ifc.Name == ifName {
|
||||
return ifc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, ifName)
|
||||
}
|
||||
|
||||
func (v *Net) getInterface(ifName string) (*transport.Interface, error) {
|
||||
v.mutex.RLock()
|
||||
defer v.mutex.RUnlock()
|
||||
|
||||
return v._getInterface(ifName)
|
||||
}
|
||||
|
||||
// InterfaceByIndex returns the interface specified by index.
|
||||
//
|
||||
// On Solaris, it returns one of the logical network interfaces
|
||||
// sharing the logical data link; for more precision use
|
||||
// InterfaceByName.
|
||||
func (v *Net) InterfaceByIndex(index int) (*transport.Interface, error) {
|
||||
for _, ifc := range v.interfaces {
|
||||
if ifc.Index == index {
|
||||
return ifc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: index=%d", transport.ErrInterfaceNotFound, index)
|
||||
}
|
||||
|
||||
// InterfaceByName returns the interface specified by name.
|
||||
func (v *Net) InterfaceByName(ifName string) (*transport.Interface, error) {
|
||||
return v.getInterface(ifName)
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (v *Net) getAllIPAddrs(ipv6 bool) []net.IP {
|
||||
ips := []net.IP{}
|
||||
|
||||
for _, ifc := range v.interfaces {
|
||||
addrs, err := ifc.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
if ipNet, ok := addr.(*net.IPNet); ok {
|
||||
ip = ipNet.IP
|
||||
} else if ipAddr, ok := addr.(*net.IPAddr); ok {
|
||||
ip = ipAddr.IP
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if !ipv6 {
|
||||
if ip.To4() != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ips
|
||||
}
|
||||
|
||||
func (v *Net) setRouter(r *Router) error {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
v.router = r
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Net) onInboundChunk(c Chunk) {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
if c.Network() == udp {
|
||||
if conn, ok := v.udpConns.find(c.DestinationAddr()); ok {
|
||||
conn.onInboundChunk(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (v *Net) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
// validate network
|
||||
if network != udp && network != udp4 {
|
||||
return nil, fmt.Errorf("%w: %s", errUnexpectedNetwork, network)
|
||||
}
|
||||
|
||||
if locAddr == nil {
|
||||
locAddr = &net.UDPAddr{
|
||||
IP: net.IPv4zero,
|
||||
}
|
||||
} else if locAddr.IP == nil {
|
||||
locAddr.IP = net.IPv4zero
|
||||
}
|
||||
|
||||
// validate address. do we have that address?
|
||||
if !v.hasIPAddr(locAddr.IP) {
|
||||
return nil, &net.OpError{
|
||||
Op: "listen",
|
||||
Net: network,
|
||||
Addr: locAddr,
|
||||
Err: fmt.Errorf("bind: %w", errCantAssignRequestedAddr),
|
||||
}
|
||||
}
|
||||
|
||||
if locAddr.Port == 0 {
|
||||
// choose randomly from the range between 5000 and 5999
|
||||
port, err := v.assignPort(locAddr.IP, 5000, 5999)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{
|
||||
Op: "listen",
|
||||
Net: network,
|
||||
Addr: locAddr,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
locAddr.Port = port
|
||||
} else if _, ok := v.udpConns.find(locAddr); ok {
|
||||
return nil, &net.OpError{
|
||||
Op: "listen",
|
||||
Net: network,
|
||||
Addr: locAddr,
|
||||
Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := newUDPConn(locAddr, remAddr, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = v.udpConns.insert(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// ListenPacket announces on the local network address.
|
||||
func (v *Net) ListenPacket(network string, address string) (net.PacketConn, error) {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
locAddr, err := v.ResolveUDPAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v._dialUDP(network, locAddr, nil)
|
||||
}
|
||||
|
||||
// ListenUDP acts like ListenPacket for UDP networks.
|
||||
func (v *Net) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
return v._dialUDP(network, locAddr, nil)
|
||||
}
|
||||
|
||||
// DialUDP acts like Dial for UDP networks.
|
||||
func (v *Net) DialUDP(network string, locAddr, remAddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
return v._dialUDP(network, locAddr, remAddr)
|
||||
}
|
||||
|
||||
// Dial connects to the address on the named network.
|
||||
func (v *Net) Dial(network string, address string) (net.Conn, error) {
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
remAddr, err := v.ResolveUDPAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine source address
|
||||
srcIP := v.determineSourceIP(nil, remAddr.IP)
|
||||
|
||||
locAddr := &net.UDPAddr{IP: srcIP, Port: 0}
|
||||
|
||||
return v._dialUDP(network, locAddr, remAddr)
|
||||
}
|
||||
|
||||
// ResolveIPAddr returns an address of IP end point.
|
||||
func (v *Net) ResolveIPAddr(_, address string) (*net.IPAddr, error) {
|
||||
var err error
|
||||
|
||||
// Check if host is a domain name
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
address = strings.ToLower(address)
|
||||
if address == "localhost" {
|
||||
ip = net.IPv4(127, 0, 0, 1)
|
||||
} else {
|
||||
// host is a domain name. resolve IP address by the name
|
||||
if v.router == nil {
|
||||
return nil, errNoRouterLinked
|
||||
}
|
||||
|
||||
ip, err = v.router.resolver.lookUp(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &net.IPAddr{
|
||||
IP: ip,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ResolveUDPAddr returns an address of UDP end point.
|
||||
func (v *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) {
|
||||
if network != udp && network != udp4 {
|
||||
return nil, fmt.Errorf("%w %s", errUnknownNetwork, network)
|
||||
}
|
||||
|
||||
host, sPort, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipAddress, err := v.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(sPort)
|
||||
if err != nil {
|
||||
return nil, errInvalidPortNumber
|
||||
}
|
||||
|
||||
udpAddr := &net.UDPAddr{
|
||||
IP: ipAddress.IP,
|
||||
Zone: ipAddress.Zone,
|
||||
Port: port,
|
||||
}
|
||||
|
||||
return udpAddr, nil
|
||||
}
|
||||
|
||||
// ResolveTCPAddr returns an address of TCP end point.
|
||||
func (v *Net) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) {
|
||||
if network != udp && network != "udp4" {
|
||||
return nil, fmt.Errorf("%w %s", errUnknownNetwork, network)
|
||||
}
|
||||
|
||||
host, sPort, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipAddr, err := v.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(sPort)
|
||||
if err != nil {
|
||||
return nil, errInvalidPortNumber
|
||||
}
|
||||
|
||||
udpAddr := &net.TCPAddr{
|
||||
IP: ipAddr.IP,
|
||||
Zone: ipAddr.Zone,
|
||||
Port: port,
|
||||
}
|
||||
|
||||
return udpAddr, nil
|
||||
}
|
||||
|
||||
func (v *Net) write(c Chunk) error {
|
||||
if c.Network() == udp {
|
||||
if udp, ok := c.(*chunkUDP); ok {
|
||||
if c.getDestinationIP().IsLoopback() {
|
||||
if conn, ok := v.udpConns.find(udp.DestinationAddr()); ok {
|
||||
conn.onInboundChunk(udp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return errUnexpectedTypeSwitchFailure
|
||||
}
|
||||
}
|
||||
|
||||
if v.router == nil {
|
||||
return errNoRouterLinked
|
||||
}
|
||||
|
||||
v.router.push(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Net) onClosed(addr net.Addr) {
|
||||
if addr.Network() == udp {
|
||||
//nolint:errcheck
|
||||
v.udpConns.delete(addr) // #nosec
|
||||
}
|
||||
}
|
||||
|
||||
// This method determines the srcIP based on the dstIP when locIP
|
||||
// is any IP address ("0.0.0.0" or "::"). If locIP is a non-any addr,
|
||||
// this method simply returns locIP.
|
||||
// caller must hold the mutex
|
||||
func (v *Net) determineSourceIP(locIP, dstIP net.IP) net.IP {
|
||||
if locIP != nil && !locIP.IsUnspecified() {
|
||||
return locIP
|
||||
}
|
||||
|
||||
var srcIP net.IP
|
||||
|
||||
if dstIP.IsLoopback() {
|
||||
srcIP = net.ParseIP("127.0.0.1")
|
||||
} else {
|
||||
ifc, err2 := v._getInterface("eth0")
|
||||
if err2 != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
addrs, err2 := ifc.Addrs()
|
||||
if err2 != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var findIPv4 bool
|
||||
if locIP != nil {
|
||||
findIPv4 = (locIP.To4() != nil)
|
||||
} else {
|
||||
findIPv4 = (dstIP.To4() != nil)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
ip := addr.(*net.IPNet).IP //nolint:forcetypeassert
|
||||
if findIPv4 {
|
||||
if ip.To4() != nil {
|
||||
srcIP = ip
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if ip.To4() == nil {
|
||||
srcIP = ip
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return srcIP
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (v *Net) hasIPAddr(ip net.IP) bool { //nolint:gocognit
|
||||
for _, ifc := range v.interfaces {
|
||||
if addrs, err := ifc.Addrs(); err == nil {
|
||||
for _, addr := range addrs {
|
||||
var locIP net.IP
|
||||
if ipNet, ok := addr.(*net.IPNet); ok {
|
||||
locIP = ipNet.IP
|
||||
} else if ipAddr, ok := addr.(*net.IPAddr); ok {
|
||||
locIP = ipAddr.IP
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
switch ip.String() {
|
||||
case "0.0.0.0":
|
||||
if locIP.To4() != nil {
|
||||
return true
|
||||
}
|
||||
case "::":
|
||||
if locIP.To4() == nil {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
if locIP.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (v *Net) allocateLocalAddr(ip net.IP, port int) error {
|
||||
// gather local IP addresses to bind
|
||||
var ips []net.IP
|
||||
if ip.IsUnspecified() {
|
||||
ips = v.getAllIPAddrs(ip.To4() == nil)
|
||||
} else if v.hasIPAddr(ip) {
|
||||
ips = []net.IP{ip}
|
||||
}
|
||||
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf("%w %s", errBindFailedFor, ip.String())
|
||||
}
|
||||
|
||||
// check if all these transport addresses are not in use
|
||||
for _, ip2 := range ips {
|
||||
addr := &net.UDPAddr{
|
||||
IP: ip2,
|
||||
Port: port,
|
||||
}
|
||||
if _, ok := v.udpConns.find(addr); ok {
|
||||
return &net.OpError{
|
||||
Op: "bind",
|
||||
Net: udp,
|
||||
Addr: addr,
|
||||
Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (v *Net) assignPort(ip net.IP, start, end int) (int, error) {
|
||||
// choose randomly from the range between start and end (inclusive)
|
||||
if end < start {
|
||||
return -1, errEndPortLessThanStart
|
||||
}
|
||||
|
||||
space := end + 1 - start
|
||||
offset := rand.Intn(space) //nolint:gosec
|
||||
for i := 0; i < space; i++ {
|
||||
port := ((offset + i) % space) + start
|
||||
|
||||
err := v.allocateLocalAddr(ip, port)
|
||||
if err == nil {
|
||||
return port, nil
|
||||
}
|
||||
}
|
||||
|
||||
return -1, errPortSpaceExhausted
|
||||
}
|
||||
|
||||
func (v *Net) getStaticIPs() []net.IP {
|
||||
return v.staticIPs
|
||||
}
|
||||
|
||||
// NetConfig is a bag of configuration parameters passed to NewNet().
|
||||
type NetConfig struct {
|
||||
// StaticIPs is an array of static IP addresses to be assigned for this Net.
|
||||
// If no static IP address is given, the router will automatically assign
|
||||
// an IP address.
|
||||
StaticIPs []string
|
||||
|
||||
// StaticIP is deprecated. Use StaticIPs.
|
||||
StaticIP string
|
||||
}
|
||||
|
||||
// NewNet creates an instance of a virtual network.
|
||||
//
|
||||
// By design, it always have lo0 and eth0 interfaces.
|
||||
// The lo0 has the address 127.0.0.1 assigned by default.
|
||||
// IP address for eth0 will be assigned when this Net is added to a router.
|
||||
func NewNet(config *NetConfig) (*Net, error) {
|
||||
lo0 := transport.NewInterface(net.Interface{
|
||||
Index: 1,
|
||||
MTU: 16384,
|
||||
Name: lo0String,
|
||||
HardwareAddr: nil,
|
||||
Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
|
||||
})
|
||||
lo0.AddAddress(&net.IPNet{
|
||||
IP: net.ParseIP("127.0.0.1"),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
})
|
||||
|
||||
eth0 := transport.NewInterface(net.Interface{
|
||||
Index: 2,
|
||||
MTU: 1500,
|
||||
Name: "eth0",
|
||||
HardwareAddr: newMACAddress(),
|
||||
Flags: net.FlagUp | net.FlagMulticast,
|
||||
})
|
||||
|
||||
var staticIPs []net.IP
|
||||
for _, ipStr := range config.StaticIPs {
|
||||
if ip := net.ParseIP(ipStr); ip != nil {
|
||||
staticIPs = append(staticIPs, ip)
|
||||
}
|
||||
}
|
||||
if len(config.StaticIP) > 0 {
|
||||
if ip := net.ParseIP(config.StaticIP); ip != nil {
|
||||
staticIPs = append(staticIPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return &Net{
|
||||
interfaces: []*transport.Interface{lo0, eth0},
|
||||
staticIPs: staticIPs,
|
||||
udpConns: newUDPConnMap(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DialTCP acts like Dial for TCP networks.
|
||||
func (v *Net) DialTCP(string, *net.TCPAddr, *net.TCPAddr) (transport.TCPConn, error) {
|
||||
return nil, transport.ErrNotSupported
|
||||
}
|
||||
|
||||
// ListenTCP acts like Listen for TCP networks.
|
||||
func (v *Net) ListenTCP(string, *net.TCPAddr) (transport.TCPListener, error) {
|
||||
return nil, transport.ErrNotSupported
|
||||
}
|
||||
|
||||
// CreateDialer creates an instance of vnet.Dialer
|
||||
func (v *Net) CreateDialer(d *net.Dialer) transport.Dialer {
|
||||
return &dialer{
|
||||
dialer: d,
|
||||
net: v,
|
||||
}
|
||||
}
|
||||
|
||||
type dialer struct {
|
||||
dialer *net.Dialer
|
||||
net *Net
|
||||
}
|
||||
|
||||
func (d *dialer) Dial(network, address string) (net.Conn, error) {
|
||||
return d.net.Dial(network, address)
|
||||
}
|
||||
92
server/vendor/github.com/pion/transport/v2/vnet/resolver.go
generated
vendored
Normal file
92
server/vendor/github.com/pion/transport/v2/vnet/resolver.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
var (
|
||||
errHostnameEmpty = errors.New("host name must not be empty")
|
||||
errFailedToParseIPAddr = errors.New("failed to parse IP address")
|
||||
)
|
||||
|
||||
type resolverConfig struct {
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
parent *resolver // read-only
|
||||
hosts map[string]net.IP // requires mutex
|
||||
mutex sync.RWMutex // thread-safe
|
||||
log logging.LeveledLogger // read-only
|
||||
}
|
||||
|
||||
func newResolver(config *resolverConfig) *resolver {
|
||||
r := &resolver{
|
||||
hosts: map[string]net.IP{},
|
||||
log: config.LoggerFactory.NewLogger("vnet"),
|
||||
}
|
||||
|
||||
if err := r.addHost("localhost", "127.0.0.1"); err != nil {
|
||||
r.log.Warn("failed to add localhost to resolver")
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *resolver) setParent(parent *resolver) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
r.parent = parent
|
||||
}
|
||||
|
||||
func (r *resolver) addHost(name string, ipAddr string) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
if len(name) == 0 {
|
||||
return errHostnameEmpty
|
||||
}
|
||||
ip := net.ParseIP(ipAddr)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("%w \"%s\"", errFailedToParseIPAddr, ipAddr)
|
||||
}
|
||||
r.hosts[name] = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resolver) lookUp(hostName string) (net.IP, error) {
|
||||
ip := func() net.IP {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
|
||||
if ip2, ok := r.hosts[hostName]; ok {
|
||||
return ip2
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// mutex must be unlocked before calling into parent resolver
|
||||
|
||||
if r.parent != nil {
|
||||
return r.parent.lookUp(hostName)
|
||||
}
|
||||
|
||||
return nil, &net.DNSError{
|
||||
Err: "host not found",
|
||||
Name: hostName,
|
||||
Server: "vnet resolver",
|
||||
IsTimeout: false,
|
||||
IsTemporary: false,
|
||||
}
|
||||
}
|
||||
621
server/vendor/github.com/pion/transport/v2/vnet/router.go
generated
vendored
Normal file
621
server/vendor/github.com/pion/transport/v2/vnet/router.go
generated
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRouterQueueSize = 0 // unlimited
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidLocalIPinStaticIPs = errors.New("invalid local IP in StaticIPs")
|
||||
errLocalIPBeyondStaticIPsSubset = errors.New("mapped in StaticIPs is beyond subnet")
|
||||
errLocalIPNoStaticsIPsAssociated = errors.New("all StaticIPs must have associated local IPs")
|
||||
errRouterAlreadyStarted = errors.New("router already started")
|
||||
errRouterAlreadyStopped = errors.New("router already stopped")
|
||||
errStaticIPisBeyondSubnet = errors.New("static IP is beyond subnet")
|
||||
errAddressSpaceExhausted = errors.New("address space exhausted")
|
||||
errNoIPAddrEth0 = errors.New("no IP address is assigned for eth0")
|
||||
)
|
||||
|
||||
// Generate a unique router name
|
||||
var assignRouterName = func() func() string { //nolint:gochecknoglobals
|
||||
var routerIDCtr uint64
|
||||
|
||||
return func() string {
|
||||
n := atomic.AddUint64(&routerIDCtr, 1)
|
||||
return fmt.Sprintf("router%d", n)
|
||||
}
|
||||
}()
|
||||
|
||||
// RouterConfig ...
|
||||
type RouterConfig struct {
|
||||
// Name of router. If not specified, a unique name will be assigned.
|
||||
Name string
|
||||
// CIDR notation, like "192.0.2.0/24"
|
||||
CIDR string
|
||||
// StaticIPs is an array of static IP addresses to be assigned for this router.
|
||||
// If no static IP address is given, the router will automatically assign
|
||||
// an IP address.
|
||||
// This will be ignored if this router is the root.
|
||||
StaticIPs []string
|
||||
// StaticIP is deprecated. Use StaticIPs.
|
||||
StaticIP string
|
||||
// Internal queue size
|
||||
QueueSize int
|
||||
// Effective only when this router has a parent router
|
||||
NATType *NATType
|
||||
// Minimum Delay
|
||||
MinDelay time.Duration
|
||||
// Max Jitter
|
||||
MaxJitter time.Duration
|
||||
// Logger factory
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
// NIC is a network interface controller that interfaces Router
|
||||
type NIC interface {
|
||||
getInterface(ifName string) (*transport.Interface, error)
|
||||
onInboundChunk(c Chunk)
|
||||
getStaticIPs() []net.IP
|
||||
setRouter(r *Router) error
|
||||
}
|
||||
|
||||
// ChunkFilter is a handler users can add to filter chunks.
|
||||
// If the filter returns false, the packet will be dropped.
|
||||
type ChunkFilter func(c Chunk) bool
|
||||
|
||||
// Router ...
|
||||
type Router struct {
|
||||
name string // read-only
|
||||
interfaces []*transport.Interface // read-only
|
||||
ipv4Net *net.IPNet // read-only
|
||||
staticIPs []net.IP // read-only
|
||||
staticLocalIPs map[string]net.IP // read-only,
|
||||
lastID byte // requires mutex [x], used to assign the last digit of IPv4 address
|
||||
queue *chunkQueue // read-only
|
||||
parent *Router // read-only
|
||||
children []*Router // read-only
|
||||
natType *NATType // read-only
|
||||
nat *networkAddressTranslator // read-only
|
||||
nics map[string]NIC // read-only
|
||||
stopFunc func() // requires mutex [x]
|
||||
resolver *resolver // read-only
|
||||
chunkFilters []ChunkFilter // requires mutex [x]
|
||||
minDelay time.Duration // requires mutex [x]
|
||||
maxJitter time.Duration // requires mutex [x]
|
||||
mutex sync.RWMutex // thread-safe
|
||||
pushCh chan struct{} // writer requires mutex
|
||||
loggerFactory logging.LoggerFactory // read-only
|
||||
log logging.LeveledLogger // read-only
|
||||
}
|
||||
|
||||
// NewRouter ...
|
||||
func NewRouter(config *RouterConfig) (*Router, error) {
|
||||
loggerFactory := config.LoggerFactory
|
||||
log := loggerFactory.NewLogger("vnet")
|
||||
|
||||
_, ipv4Net, err := net.ParseCIDR(config.CIDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queueSize := defaultRouterQueueSize
|
||||
if config.QueueSize > 0 {
|
||||
queueSize = config.QueueSize
|
||||
}
|
||||
|
||||
// set up network interface, lo0
|
||||
lo0 := transport.NewInterface(net.Interface{
|
||||
Index: 1,
|
||||
MTU: 16384,
|
||||
Name: lo0String,
|
||||
HardwareAddr: nil,
|
||||
Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
|
||||
})
|
||||
lo0.AddAddress(&net.IPAddr{IP: net.ParseIP("127.0.0.1"), Zone: ""})
|
||||
|
||||
// set up network interface, eth0
|
||||
eth0 := transport.NewInterface(net.Interface{
|
||||
Index: 2,
|
||||
MTU: 1500,
|
||||
Name: "eth0",
|
||||
HardwareAddr: newMACAddress(),
|
||||
Flags: net.FlagUp | net.FlagMulticast,
|
||||
})
|
||||
|
||||
// local host name resolver
|
||||
resolver := newResolver(&resolverConfig{
|
||||
LoggerFactory: config.LoggerFactory,
|
||||
})
|
||||
|
||||
name := config.Name
|
||||
if len(name) == 0 {
|
||||
name = assignRouterName()
|
||||
}
|
||||
|
||||
var staticIPs []net.IP
|
||||
staticLocalIPs := map[string]net.IP{}
|
||||
for _, ipStr := range config.StaticIPs {
|
||||
ipPair := strings.Split(ipStr, "/")
|
||||
if ip := net.ParseIP(ipPair[0]); ip != nil {
|
||||
if len(ipPair) > 1 {
|
||||
locIP := net.ParseIP(ipPair[1])
|
||||
if locIP == nil {
|
||||
return nil, errInvalidLocalIPinStaticIPs
|
||||
}
|
||||
if !ipv4Net.Contains(locIP) {
|
||||
return nil, fmt.Errorf("local IP %s %w", locIP.String(), errLocalIPBeyondStaticIPsSubset)
|
||||
}
|
||||
staticLocalIPs[ip.String()] = locIP
|
||||
}
|
||||
staticIPs = append(staticIPs, ip)
|
||||
}
|
||||
}
|
||||
if len(config.StaticIP) > 0 {
|
||||
log.Warn("StaticIP is deprecated. Use StaticIPs instead")
|
||||
if ip := net.ParseIP(config.StaticIP); ip != nil {
|
||||
staticIPs = append(staticIPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
if nStaticLocal := len(staticLocalIPs); nStaticLocal > 0 {
|
||||
if nStaticLocal != len(staticIPs) {
|
||||
return nil, errLocalIPNoStaticsIPsAssociated
|
||||
}
|
||||
}
|
||||
|
||||
return &Router{
|
||||
name: name,
|
||||
interfaces: []*transport.Interface{lo0, eth0},
|
||||
ipv4Net: ipv4Net,
|
||||
staticIPs: staticIPs,
|
||||
staticLocalIPs: staticLocalIPs,
|
||||
queue: newChunkQueue(queueSize, 0),
|
||||
natType: config.NATType,
|
||||
nics: map[string]NIC{},
|
||||
resolver: resolver,
|
||||
minDelay: config.MinDelay,
|
||||
maxJitter: config.MaxJitter,
|
||||
pushCh: make(chan struct{}, 1),
|
||||
loggerFactory: loggerFactory,
|
||||
log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (r *Router) getInterfaces() ([]*transport.Interface, error) {
|
||||
if len(r.interfaces) == 0 {
|
||||
return nil, fmt.Errorf("%w is available", errNoInterface)
|
||||
}
|
||||
|
||||
return r.interfaces, nil
|
||||
}
|
||||
|
||||
func (r *Router) getInterface(ifName string) (*transport.Interface, error) {
|
||||
r.mutex.RLock()
|
||||
defer r.mutex.RUnlock()
|
||||
|
||||
ifs, err := r.getInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifc := range ifs {
|
||||
if ifc.Name == ifName {
|
||||
return ifc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, ifName)
|
||||
}
|
||||
|
||||
// Start ...
|
||||
func (r *Router) Start() error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
if r.stopFunc != nil {
|
||||
return errRouterAlreadyStarted
|
||||
}
|
||||
|
||||
cancelCh := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
loop:
|
||||
for {
|
||||
d, err := r.processChunks()
|
||||
if err != nil {
|
||||
r.log.Errorf("[%s] %s", r.name, err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
if d <= 0 {
|
||||
select {
|
||||
case <-r.pushCh:
|
||||
case <-cancelCh:
|
||||
break loop
|
||||
}
|
||||
} else {
|
||||
t := time.NewTimer(d)
|
||||
select {
|
||||
case <-t.C:
|
||||
case <-cancelCh:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
r.stopFunc = func() {
|
||||
close(cancelCh)
|
||||
}
|
||||
|
||||
for _, child := range r.children {
|
||||
if err := child.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop ...
|
||||
func (r *Router) Stop() error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
if r.stopFunc == nil {
|
||||
return errRouterAlreadyStopped
|
||||
}
|
||||
|
||||
for _, router := range r.children {
|
||||
r.mutex.Unlock()
|
||||
err := router.Stop()
|
||||
r.mutex.Lock()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.stopFunc()
|
||||
r.stopFunc = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (r *Router) addNIC(nic NIC) error {
|
||||
ifc, err := nic.getInterface("eth0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ips []net.IP
|
||||
|
||||
if ips = nic.getStaticIPs(); len(ips) == 0 {
|
||||
// assign an IP address
|
||||
ip, err2 := r.assignIPAddress()
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if !r.ipv4Net.Contains(ip) {
|
||||
return fmt.Errorf("%w: %s", errStaticIPisBeyondSubnet, r.ipv4Net.String())
|
||||
}
|
||||
|
||||
ifc.AddAddress(&net.IPNet{
|
||||
IP: ip,
|
||||
Mask: r.ipv4Net.Mask,
|
||||
})
|
||||
|
||||
r.nics[ip.String()] = nic
|
||||
}
|
||||
|
||||
return nic.setRouter(r)
|
||||
}
|
||||
|
||||
// AddRouter adds a child Router.
|
||||
func (r *Router) AddRouter(router *Router) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
// Router is a NIC. Add it as a NIC so that packets are routed to this child
|
||||
// router.
|
||||
err := r.addNIC(router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = router.setRouter(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.children = append(r.children, router)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddChildRouter is like AddRouter, but does not add the child routers NIC to
|
||||
// the parent. This has to be done manually by calling AddNet, which allows to
|
||||
// use a wrapper around the subrouters NIC.
|
||||
// AddNet MUST be called before AddChildRouter.
|
||||
func (r *Router) AddChildRouter(router *Router) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
if err := router.setRouter(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.children = append(r.children, router)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddNet ...
|
||||
func (r *Router) AddNet(nic NIC) error {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
return r.addNIC(nic)
|
||||
}
|
||||
|
||||
// AddHost adds a mapping of hostname and an IP address to the local resolver.
|
||||
func (r *Router) AddHost(hostName string, ipAddr string) error {
|
||||
return r.resolver.addHost(hostName, ipAddr)
|
||||
}
|
||||
|
||||
// AddChunkFilter adds a filter for chunks traversing this router.
|
||||
// You may add more than one filter. The filters are called in the order of this method call.
|
||||
// If a chunk is dropped by a filter, subsequent filter will not receive the chunk.
|
||||
func (r *Router) AddChunkFilter(filter ChunkFilter) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
r.chunkFilters = append(r.chunkFilters, filter)
|
||||
}
|
||||
|
||||
// caller should hold the mutex
|
||||
func (r *Router) assignIPAddress() (net.IP, error) {
|
||||
// See: https://stackoverflow.com/questions/14915188/ip-address-ending-with-zero
|
||||
|
||||
if r.lastID == 0xfe {
|
||||
return nil, errAddressSpaceExhausted
|
||||
}
|
||||
|
||||
ip := make(net.IP, 4)
|
||||
copy(ip, r.ipv4Net.IP[:3])
|
||||
r.lastID++
|
||||
ip[3] = r.lastID
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func (r *Router) push(c Chunk) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
r.log.Debugf("[%s] route %s", r.name, c.String())
|
||||
if r.stopFunc != nil {
|
||||
c.setTimestamp()
|
||||
if r.queue.push(c) {
|
||||
select {
|
||||
case r.pushCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
r.log.Warnf("[%s] queue was full. dropped a chunk", r.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) processChunks() (time.Duration, error) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
// Introduce jitter by delaying the processing of chunks.
|
||||
if r.maxJitter > 0 {
|
||||
jitter := time.Duration(rand.Int63n(int64(r.maxJitter))) //nolint:gosec
|
||||
time.Sleep(jitter)
|
||||
}
|
||||
|
||||
// cutOff
|
||||
// v min delay
|
||||
// |<--->|
|
||||
// +------------:--
|
||||
// |OOOOOOXXXXX : --> time
|
||||
// +------------:--
|
||||
// |<--->| now
|
||||
// due
|
||||
|
||||
enteredAt := time.Now()
|
||||
cutOff := enteredAt.Add(-r.minDelay)
|
||||
|
||||
var d time.Duration // the next sleep duration
|
||||
|
||||
for {
|
||||
d = 0
|
||||
|
||||
c := r.queue.peek()
|
||||
if c == nil {
|
||||
break // no more chunk in the queue
|
||||
}
|
||||
|
||||
// check timestamp to find if the chunk is due
|
||||
if c.getTimestamp().After(cutOff) {
|
||||
// There is one or more chunk in the queue but none of them are due.
|
||||
// Calculate the next sleep duration here.
|
||||
nextExpire := c.getTimestamp().Add(r.minDelay)
|
||||
d = nextExpire.Sub(enteredAt)
|
||||
break
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if c, ok = r.queue.pop(); !ok {
|
||||
break // no more chunk in the queue
|
||||
}
|
||||
|
||||
blocked := false
|
||||
for i := 0; i < len(r.chunkFilters); i++ {
|
||||
filter := r.chunkFilters[i]
|
||||
if !filter(c) {
|
||||
blocked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if blocked {
|
||||
continue // discard
|
||||
}
|
||||
|
||||
dstIP := c.getDestinationIP()
|
||||
|
||||
// check if the destination is in our subnet
|
||||
if r.ipv4Net.Contains(dstIP) {
|
||||
// search for the destination NIC
|
||||
var nic NIC
|
||||
if nic, ok = r.nics[dstIP.String()]; !ok {
|
||||
// NIC not found. drop it.
|
||||
r.log.Debugf("[%s] %s unreachable", r.name, c.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// found the NIC, forward the chunk to the NIC.
|
||||
// call to NIC must unlock mutex
|
||||
r.mutex.Unlock()
|
||||
nic.onInboundChunk(c)
|
||||
r.mutex.Lock()
|
||||
continue
|
||||
}
|
||||
|
||||
// the destination is outside of this subnet
|
||||
// is this WAN?
|
||||
if r.parent == nil {
|
||||
// this WAN. No route for this chunk
|
||||
r.log.Debugf("[%s] no route found for %s", r.name, c.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// Pass it to the parent via NAT
|
||||
toParent, err := r.nat.translateOutbound(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if toParent == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
//nolint:godox
|
||||
/* FIXME: this implementation would introduce a duplicate packet!
|
||||
if r.nat.natType.Hairpinning {
|
||||
hairpinned, err := r.nat.translateInbound(toParent)
|
||||
if err != nil {
|
||||
r.log.Warnf("[%s] %s", r.name, err.Error())
|
||||
} else {
|
||||
go func() {
|
||||
r.push(hairpinned)
|
||||
}()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// call to parent router mutex unlock mutex
|
||||
r.mutex.Unlock()
|
||||
r.parent.push(toParent)
|
||||
r.mutex.Lock()
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// caller must hold the mutex
|
||||
func (r *Router) setRouter(parent *Router) error {
|
||||
r.parent = parent
|
||||
r.resolver.setParent(parent.resolver)
|
||||
|
||||
// when this method is called, one or more IP address has already been assigned by
|
||||
// the parent router.
|
||||
ifc, err := r.getInterface("eth0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addrs, _ := ifc.Addrs()
|
||||
if len(addrs) == 0 {
|
||||
return errNoIPAddrEth0
|
||||
}
|
||||
|
||||
mappedIPs := []net.IP{}
|
||||
localIPs := []net.IP{}
|
||||
|
||||
for _, ifcAddr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := ifcAddr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
case *net.IPAddr: // Do we really need this case?
|
||||
ip = addr.IP
|
||||
default:
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
mappedIPs = append(mappedIPs, ip)
|
||||
|
||||
if locIP := r.staticLocalIPs[ip.String()]; locIP != nil {
|
||||
localIPs = append(localIPs, locIP)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up NAT here
|
||||
if r.natType == nil {
|
||||
r.natType = &NATType{
|
||||
MappingBehavior: EndpointIndependent,
|
||||
FilteringBehavior: EndpointAddrPortDependent,
|
||||
Hairpinning: false,
|
||||
PortPreservation: false,
|
||||
MappingLifeTime: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
r.nat, err = newNAT(&natConfig{
|
||||
name: r.name,
|
||||
natType: *r.natType,
|
||||
mappedIPs: mappedIPs,
|
||||
localIPs: localIPs,
|
||||
loggerFactory: r.loggerFactory,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) onInboundChunk(c Chunk) {
|
||||
fromParent, err := r.nat.translateInbound(c)
|
||||
if err != nil {
|
||||
r.log.Warnf("[%s] %s", r.name, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
r.push(fromParent)
|
||||
}
|
||||
|
||||
func (r *Router) getStaticIPs() []net.IP {
|
||||
return r.staticIPs
|
||||
}
|
||||
167
server/vendor/github.com/pion/transport/v2/vnet/tbf.go
generated
vendored
Normal file
167
server/vendor/github.com/pion/transport/v2/vnet/tbf.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
// Bit is a single bit
|
||||
Bit = 1
|
||||
// KBit is a kilobit
|
||||
KBit = 1000 * Bit
|
||||
// MBit is a Megabit
|
||||
MBit = 1000 * KBit
|
||||
)
|
||||
|
||||
// TokenBucketFilter implements a token bucket rate limit algorithm.
|
||||
type TokenBucketFilter struct {
|
||||
NIC
|
||||
currentTokensInBucket float64
|
||||
c chan Chunk
|
||||
queue *chunkQueue
|
||||
queueSize int // in bytes
|
||||
|
||||
mutex sync.Mutex
|
||||
rate int
|
||||
maxBurst int
|
||||
minRefillDuration time.Duration
|
||||
|
||||
wg sync.WaitGroup
|
||||
done chan struct{}
|
||||
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// TBFOption is the option type to configure a TokenBucketFilter
|
||||
type TBFOption func(*TokenBucketFilter) TBFOption
|
||||
|
||||
// TBFQueueSizeInBytes sets the max number of bytes waiting in the queue. Can
|
||||
// only be set in constructor before using the TBF.
|
||||
func TBFQueueSizeInBytes(bytes int) TBFOption {
|
||||
return func(t *TokenBucketFilter) TBFOption {
|
||||
prev := t.queueSize
|
||||
t.queueSize = bytes
|
||||
return TBFQueueSizeInBytes(prev)
|
||||
}
|
||||
}
|
||||
|
||||
// TBFRate sets the bit rate of a TokenBucketFilter
|
||||
func TBFRate(rate int) TBFOption {
|
||||
return func(t *TokenBucketFilter) TBFOption {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
previous := t.rate
|
||||
t.rate = rate
|
||||
return TBFRate(previous)
|
||||
}
|
||||
}
|
||||
|
||||
// TBFMaxBurst sets the bucket size of the token bucket filter. This is the
|
||||
// maximum size that can instantly leave the filter, if the bucket is full.
|
||||
func TBFMaxBurst(size int) TBFOption {
|
||||
return func(t *TokenBucketFilter) TBFOption {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
previous := t.maxBurst
|
||||
t.maxBurst = size
|
||||
return TBFMaxBurst(previous)
|
||||
}
|
||||
}
|
||||
|
||||
// Set updates a setting on the token bucket filter
|
||||
func (t *TokenBucketFilter) Set(opts ...TBFOption) (previous TBFOption) {
|
||||
for _, opt := range opts {
|
||||
previous = opt(t)
|
||||
}
|
||||
return previous
|
||||
}
|
||||
|
||||
// NewTokenBucketFilter creates and starts a new TokenBucketFilter
|
||||
func NewTokenBucketFilter(n NIC, opts ...TBFOption) (*TokenBucketFilter, error) {
|
||||
tbf := &TokenBucketFilter{
|
||||
NIC: n,
|
||||
currentTokensInBucket: 0,
|
||||
c: make(chan Chunk),
|
||||
queue: nil,
|
||||
queueSize: 50000,
|
||||
mutex: sync.Mutex{},
|
||||
rate: 1 * MBit,
|
||||
maxBurst: 8 * KBit,
|
||||
minRefillDuration: 100 * time.Millisecond,
|
||||
wg: sync.WaitGroup{},
|
||||
done: make(chan struct{}),
|
||||
log: logging.NewDefaultLoggerFactory().NewLogger("tbf"),
|
||||
}
|
||||
tbf.Set(opts...)
|
||||
tbf.queue = newChunkQueue(0, tbf.queueSize)
|
||||
tbf.wg.Add(1)
|
||||
go tbf.run()
|
||||
return tbf, nil
|
||||
}
|
||||
|
||||
func (t *TokenBucketFilter) onInboundChunk(c Chunk) {
|
||||
t.c <- c
|
||||
}
|
||||
|
||||
func (t *TokenBucketFilter) run() {
|
||||
defer t.wg.Done()
|
||||
|
||||
t.refillTokens(t.minRefillDuration)
|
||||
lastRefill := time.Now()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-t.done:
|
||||
t.drainQueue()
|
||||
return
|
||||
case chunk := <-t.c:
|
||||
if time.Since(lastRefill) > t.minRefillDuration {
|
||||
t.refillTokens(time.Since(lastRefill))
|
||||
lastRefill = time.Now()
|
||||
}
|
||||
t.queue.push(chunk)
|
||||
t.drainQueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TokenBucketFilter) refillTokens(dt time.Duration) {
|
||||
m := 1000.0 / float64(dt.Milliseconds())
|
||||
add := (float64(t.rate) / m) / 8.0
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
t.currentTokensInBucket = math.Min(float64(t.maxBurst), t.currentTokensInBucket+add)
|
||||
t.log.Tracef("add=(%v / %v) / 8 = %v, currentTokensInBucket=%v, maxBurst=%v", t.rate, m, add, t.currentTokensInBucket, t.maxBurst)
|
||||
}
|
||||
|
||||
func (t *TokenBucketFilter) drainQueue() {
|
||||
for {
|
||||
next := t.queue.peek()
|
||||
if next == nil {
|
||||
break
|
||||
}
|
||||
tokens := float64(len(next.UserData()))
|
||||
if t.currentTokensInBucket < tokens {
|
||||
t.log.Tracef("currentTokensInBucket=%v, tokens=%v, stop drain", t.currentTokensInBucket, tokens)
|
||||
break
|
||||
}
|
||||
t.log.Tracef("currentTokensInBucket=%v, tokens=%v, pop chunk", t.currentTokensInBucket, tokens)
|
||||
t.queue.pop()
|
||||
t.NIC.onInboundChunk(next)
|
||||
t.currentTokensInBucket -= tokens
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes and stops the token bucket filter queue
|
||||
func (t *TokenBucketFilter) Close() error {
|
||||
close(t.done)
|
||||
t.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
223
server/vendor/github.com/pion/transport/v2/vnet/udpproxy.go
generated
vendored
Normal file
223
server/vendor/github.com/pion/transport/v2/vnet/udpproxy.go
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UDPProxy is a proxy between real server(net.UDPConn) and vnet.UDPConn.
|
||||
//
|
||||
// High level design:
|
||||
//
|
||||
// ..............................................
|
||||
// : Virtual Network (vnet) :
|
||||
// : :
|
||||
// +-------+ * 1 +----+ +--------+ :
|
||||
// | :App |------------>|:Net|--o<-----|:Router | .............................
|
||||
// +-------+ +----+ | | : UDPProxy :
|
||||
// : | | +----+ +---------+ +---------+ +--------+
|
||||
// : | |--->o--|:Net|-->o-| vnet. |-->o-| net. |--->-| :Real |
|
||||
// : | | +----+ | UDPConn | | UDPConn | | Server |
|
||||
// : | | : +---------+ +---------+ +--------+
|
||||
// : | | ............................:
|
||||
// : +--------+ :
|
||||
// ...............................................
|
||||
type UDPProxy struct {
|
||||
// The router bind to.
|
||||
router *Router
|
||||
|
||||
// Each vnet source, bind to a real socket to server.
|
||||
// key is real server addr, which is net.Addr
|
||||
// value is *aUDPProxyWorker
|
||||
workers sync.Map
|
||||
|
||||
// For each endpoint, we never know when to start and stop proxy,
|
||||
// so we stop the endpoint when timeout.
|
||||
timeout time.Duration
|
||||
|
||||
// For utest, to mock the target real server.
|
||||
// Optional, use the address of received client packet.
|
||||
mockRealServerAddr *net.UDPAddr
|
||||
}
|
||||
|
||||
// NewProxy create a proxy, the router for this proxy belongs/bind to. If need to proxy for
|
||||
// please create a new proxy for each router. For all addresses we proxy, we will create a
|
||||
// vnet.Net in this router and proxy all packets.
|
||||
func NewProxy(router *Router) (*UDPProxy, error) {
|
||||
v := &UDPProxy{router: router, timeout: 2 * time.Minute}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Close the proxy, stop all workers.
|
||||
func (v *UDPProxy) Close() error {
|
||||
v.workers.Range(func(key, value interface{}) bool {
|
||||
_ = value.(*aUDPProxyWorker).Close() //nolint:forcetypeassert
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Proxy starts a worker for server, ignore if already started.
|
||||
func (v *UDPProxy) Proxy(client *Net, server *net.UDPAddr) error {
|
||||
// Note that even if the worker exists, it's also ok to create a same worker,
|
||||
// because the router will use the last one, and the real server will see a address
|
||||
// change event after we switch to the next worker.
|
||||
if _, ok := v.workers.Load(server.String()); ok {
|
||||
// nolint:godox // TODO: Need to restart the stopped worker?
|
||||
return nil
|
||||
}
|
||||
|
||||
// Not exists, create a new one.
|
||||
worker := &aUDPProxyWorker{
|
||||
router: v.router, mockRealServerAddr: v.mockRealServerAddr,
|
||||
}
|
||||
|
||||
// Create context for cleanup.
|
||||
var ctx context.Context
|
||||
ctx, worker.ctxDisposeCancel = context.WithCancel(context.Background())
|
||||
|
||||
v.workers.Store(server.String(), worker)
|
||||
|
||||
return worker.Proxy(ctx, client, server)
|
||||
}
|
||||
|
||||
// A proxy worker for a specified proxy server.
|
||||
type aUDPProxyWorker struct {
|
||||
router *Router
|
||||
mockRealServerAddr *net.UDPAddr
|
||||
|
||||
// Each vnet source, bind to a real socket to server.
|
||||
// key is vnet client addr, which is net.Addr
|
||||
// value is *net.UDPConn
|
||||
endpoints sync.Map
|
||||
|
||||
// For cleanup.
|
||||
ctxDisposeCancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (v *aUDPProxyWorker) Close() error {
|
||||
// Notify all goroutines to dispose.
|
||||
v.ctxDisposeCancel()
|
||||
|
||||
// Wait for all goroutines quit.
|
||||
v.wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *aUDPProxyWorker) Proxy(ctx context.Context, _ *Net, serverAddr *net.UDPAddr) error { // nolint:gocognit
|
||||
// Create vnet for real server by serverAddr.
|
||||
nw, err := NewNet(&NetConfig{
|
||||
StaticIP: serverAddr.IP.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = v.router.AddNet(nw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We must create a "same" vnet.UDPConn as the net.UDPConn,
|
||||
// which has the same ip:port, to copy packets between them.
|
||||
vnetSocket, err := nw.ListenUDP("udp4", serverAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// User stop proxy, we should close the socket.
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = vnetSocket.Close()
|
||||
}()
|
||||
|
||||
// Got new vnet client, start a new endpoint.
|
||||
findEndpointBy := func(addr net.Addr) (*net.UDPConn, error) {
|
||||
// Exists binding.
|
||||
if value, ok := v.endpoints.Load(addr.String()); ok {
|
||||
// Exists endpoint, reuse it.
|
||||
return value.(*net.UDPConn), nil //nolint:forcetypeassert
|
||||
}
|
||||
|
||||
// The real server we proxy to, for utest to mock it.
|
||||
realAddr := serverAddr
|
||||
if v.mockRealServerAddr != nil {
|
||||
realAddr = v.mockRealServerAddr
|
||||
}
|
||||
|
||||
// Got new vnet client, create new endpoint.
|
||||
realSocket, err := net.DialUDP("udp4", nil, realAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// User stop proxy, we should close the socket.
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = realSocket.Close()
|
||||
}()
|
||||
|
||||
// Bind address.
|
||||
v.endpoints.Store(addr.String(), realSocket)
|
||||
|
||||
// Got packet from real serverAddr, we should proxy it to vnet.
|
||||
v.wg.Add(1)
|
||||
go func(vnetClientAddr net.Addr) {
|
||||
defer v.wg.Done()
|
||||
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
n, _, err := realSocket.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n <= 0 {
|
||||
continue // Drop packet
|
||||
}
|
||||
|
||||
if _, err := vnetSocket.WriteTo(buf[:n], vnetClientAddr); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}(addr)
|
||||
|
||||
return realSocket, nil
|
||||
}
|
||||
|
||||
// Start a proxy goroutine.
|
||||
v.wg.Add(1)
|
||||
go func() {
|
||||
defer v.wg.Done()
|
||||
|
||||
buf := make([]byte, 1500)
|
||||
|
||||
for {
|
||||
n, addr, err := vnetSocket.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n <= 0 || addr == nil {
|
||||
continue // Drop packet
|
||||
}
|
||||
|
||||
realSocket, err := findEndpointBy(addr)
|
||||
if err != nil {
|
||||
continue // Drop packet.
|
||||
}
|
||||
|
||||
if _, err := realSocket.Write(buf[:n]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
48
server/vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go
generated
vendored
Normal file
48
server/vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Deliver directly send packet to vnet or real-server.
|
||||
// For example, we can use this API to simulate the REPLAY ATTACK.
|
||||
func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) {
|
||||
v.workers.Range(func(key, value interface{}) bool {
|
||||
if nn, err = value.(*aUDPProxyWorker).Deliver(sourceAddr, destAddr, b); err != nil {
|
||||
return false // Fail, abort.
|
||||
} else if nn == len(b) {
|
||||
return false // Done.
|
||||
}
|
||||
|
||||
return true // Deliver by next worker.
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (v *aUDPProxyWorker) Deliver(sourceAddr, _ net.Addr, b []byte) (nn int, err error) {
|
||||
addr, ok := sourceAddr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("invalid addr %v", sourceAddr) // nolint:goerr113
|
||||
}
|
||||
|
||||
// nolint:godox // TODO: Support deliver packet from real server to vnet.
|
||||
// If packet is from vnet, proxy to real server.
|
||||
var realSocket *net.UDPConn
|
||||
value, ok := v.endpoints.Load(addr.String())
|
||||
if !ok {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
realSocket = value.(*net.UDPConn) // nolint:forcetypeassert
|
||||
|
||||
// Send to real server.
|
||||
if _, err := realSocket.Write(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
5
server/vendor/github.com/pion/transport/v2/vnet/vnet.go
generated
vendored
Normal file
5
server/vendor/github.com/pion/transport/v2/vnet/vnet.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package vnet provides a virtual network layer for pion
|
||||
package vnet
|
||||
Reference in New Issue
Block a user