直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
28
server/vendor/github.com/pion/ice/v2/.gitignore
generated
vendored
Normal file
28
server/vendor/github.com/pion/ice/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/ice/v2/.golangci.yml
generated
vendored
Normal file
137
server/vendor/github.com/pion/ice/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/ice/v2/.goreleaser.yml
generated
vendored
Normal file
5
server/vendor/github.com/pion/ice/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
|
||||
68
server/vendor/github.com/pion/ice/v2/AUTHORS.txt
generated
vendored
Normal file
68
server/vendor/github.com/pion/ice/v2/AUTHORS.txt
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# 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
|
||||
Aaron France <aaron.l.france@gmail.com>
|
||||
Adam Kiss <masterada@gmail.com>
|
||||
adwpc <adwpc@hotmail.com>
|
||||
Aleksandr Razumov <ar@gortc.io>
|
||||
aler9 <46489434+aler9@users.noreply.github.com>
|
||||
Anshul <malikanshul29@gmail.com>
|
||||
Antoine Baché <antoine@tenten.app>
|
||||
ar-hosseinkhani <alireza.8096@chmail.ir>
|
||||
Artur Shellunts <shellunts.artur@gmail.com>
|
||||
Assad Obaid <assad@lap5cg901003r.se.axis.com>
|
||||
Atsushi Watanabe <atsushi.w@ieee.org>
|
||||
backkem <mail@backkem.me>
|
||||
boks1971 <raja.gobi@tutanota.com>
|
||||
buptczq <czq@qq.com>
|
||||
cgojin <gongjin21@hotmail.com>
|
||||
Chao Yuan <yuanchao0310@163.com>
|
||||
cnderrauber <zengjie9004@gmail.com>
|
||||
David Hamilton <davidhamiltron@gmail.com>
|
||||
David Zhao <david@davidzhao.com>
|
||||
David Zhao <dz@livekit.io>
|
||||
Eric Daniels <eric@erdaniels.com>
|
||||
Genteure <github@genteure.com>
|
||||
Henry <cryptix@riseup.net>
|
||||
hexiang <hexiang.zhang@chukong-inc.com>
|
||||
hn8 <10730886+hn8@users.noreply.github.com>
|
||||
Hugo Arregui <hugo.arregui@gmail.com>
|
||||
Hugo Arregui <hugo@decentraland.org>
|
||||
Jason Maldonis <jason.maldonis@i3pd.com>
|
||||
Jerko Steiner <jerko.steiner@gmail.com>
|
||||
JooYoung <qkdlql@naver.com>
|
||||
Juliusz Chroboczek <jch@irif.fr>
|
||||
Kacper Bąk <56700396+53jk1@users.noreply.github.com>
|
||||
Kevin Caffrey <kcaffrey@gmail.com>
|
||||
Konstantin Itskov <konstantin.itskov@kovits.com>
|
||||
korymiller1489 <kmiller@unwiredrevolution.com>
|
||||
Kyle Carberry <kyle@carberry.com>
|
||||
Lander Noterman <lander.noterman@basalte.be>
|
||||
Luke Curley <kixelated@gmail.com>
|
||||
Meelap Shah <meelapshah@gmail.com>
|
||||
Michael MacDonald <github@macdonald.cx>
|
||||
Michael MacDonald <mike.macdonald@savantsystems.com>
|
||||
Mikhail Bragin <misha@wiretrustee.com>
|
||||
Miroslav Šedivý <sedivy.miro@gmail.com>
|
||||
Nevio Vesic <nevio@subspace.com>
|
||||
Ori Bernstein <ori@eigenstate.org>
|
||||
Rasmus Hanning <rasmus@hanning.se>
|
||||
Robert Eperjesi <eperjesi@uber.com>
|
||||
Sam Lancia <sam@gpsm.co.uk>
|
||||
Sam Lancia <sam@vaion.com>
|
||||
San9H0 <lucian@hpcnt.com>
|
||||
Sean DuBois <seaduboi@amazon.com>
|
||||
Sean DuBois <sean@siobud.com>
|
||||
Sebastian Waisbrot <seppo0010@gmail.com>
|
||||
Sidney San Martín <sidney@s4y.us>
|
||||
Steffen Vogel <post@steffenvogel.de>
|
||||
Will Forcey <wsforc3y@gmail.com>
|
||||
Woodrow Douglass <wdouglass@carnegierobotics.com>
|
||||
Yutaka Takeda <yt0916@gmail.com>
|
||||
ZHENK <chengzhenyang@gmail.com>
|
||||
Zizheng Tai <me@zizheng.me>
|
||||
|
||||
# List of contributors not appearing in Git history
|
||||
|
||||
9
server/vendor/github.com/pion/ice/v2/LICENSE
generated
vendored
Normal file
9
server/vendor/github.com/pion/ice/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/ice/v2/README.md
generated
vendored
Normal file
34
server/vendor/github.com/pion/ice/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
Pion ICE
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of ICE</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-ice-gray.svg?longCache=true&colorB=brightgreen" alt="Pion ICE"></a>
|
||||
<a href="http://gophers.slack.com/messages/pion"><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/ice/test.yaml">
|
||||
<a href="https://pkg.go.dev/github.com/pion/ice/v2"><img src="https://pkg.go.dev/badge/github.com/pion/ice/v2.svg" alt="Go Reference"></a>
|
||||
<a href="https://codecov.io/gh/pion/ice"><img src="https://codecov.io/gh/pion/ice/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/ice"><img src="https://goreportcard.com/badge/github.com/pion/ice" 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
|
||||
158
server/vendor/github.com/pion/ice/v2/active_tcp.go
generated
vendored
Normal file
158
server/vendor/github.com/pion/ice/v2/active_tcp.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2/packetio"
|
||||
)
|
||||
|
||||
type activeTCPConn struct {
|
||||
readBuffer, writeBuffer *packetio.Buffer
|
||||
localAddr, remoteAddr atomic.Value
|
||||
closed int32
|
||||
}
|
||||
|
||||
func newActiveTCPConn(ctx context.Context, localAddress, remoteAddress string, log logging.LeveledLogger) (a *activeTCPConn) {
|
||||
a = &activeTCPConn{
|
||||
readBuffer: packetio.NewBuffer(),
|
||||
writeBuffer: packetio.NewBuffer(),
|
||||
}
|
||||
|
||||
laddr, err := getTCPAddrOnInterface(localAddress)
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&a.closed, 1)
|
||||
log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
|
||||
return
|
||||
}
|
||||
a.localAddr.Store(laddr)
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
atomic.StoreInt32(&a.closed, 1)
|
||||
}()
|
||||
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: laddr,
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, "tcp", remoteAddress)
|
||||
if err != nil {
|
||||
log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
|
||||
return
|
||||
}
|
||||
|
||||
a.remoteAddr.Store(conn.RemoteAddr())
|
||||
|
||||
go func() {
|
||||
buff := make([]byte, receiveMTU)
|
||||
|
||||
for atomic.LoadInt32(&a.closed) == 0 {
|
||||
n, err := readStreamingPacket(conn, buff)
|
||||
if err != nil {
|
||||
log.Infof("Failed to read streaming packet: %s", err)
|
||||
break
|
||||
}
|
||||
|
||||
if _, err := a.readBuffer.Write(buff[:n]); err != nil {
|
||||
log.Infof("Failed to write to buffer: %s", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
buff := make([]byte, receiveMTU)
|
||||
|
||||
for atomic.LoadInt32(&a.closed) == 0 {
|
||||
n, err := a.writeBuffer.Read(buff)
|
||||
if err != nil {
|
||||
log.Infof("Failed to read from buffer: %s", err)
|
||||
break
|
||||
}
|
||||
|
||||
if _, err = writeStreamingPacket(conn, buff[:n]); err != nil {
|
||||
log.Infof("Failed to write streaming packet: %s", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Infof("Failed to close connection: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err error) {
|
||||
if atomic.LoadInt32(&a.closed) == 1 {
|
||||
return 0, nil, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
srcAddr = a.RemoteAddr()
|
||||
n, err = a.readBuffer.Read(buff)
|
||||
return
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) WriteTo(buff []byte, _ net.Addr) (n int, err error) {
|
||||
if atomic.LoadInt32(&a.closed) == 1 {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
return a.writeBuffer.Write(buff)
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) Close() error {
|
||||
atomic.StoreInt32(&a.closed, 1)
|
||||
_ = a.readBuffer.Close()
|
||||
_ = a.writeBuffer.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) LocalAddr() net.Addr {
|
||||
if v, ok := a.localAddr.Load().(*net.TCPAddr); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) RemoteAddr() net.Addr {
|
||||
if v, ok := a.remoteAddr.Load().(*net.TCPAddr); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (a *activeTCPConn) SetDeadline(time.Time) error { return io.EOF }
|
||||
func (a *activeTCPConn) SetReadDeadline(time.Time) error { return io.EOF }
|
||||
func (a *activeTCPConn) SetWriteDeadline(time.Time) error { return io.EOF }
|
||||
|
||||
func getTCPAddrOnInterface(address string) (*net.TCPAddr, error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = l.Close()
|
||||
}()
|
||||
|
||||
tcpAddr, ok := l.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, errInvalidAddress
|
||||
}
|
||||
|
||||
return tcpAddr, nil
|
||||
}
|
||||
71
server/vendor/github.com/pion/ice/v2/addr.go
generated
vendored
Normal file
71
server/vendor/github.com/pion/ice/v2/addr.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func parseMulticastAnswerAddr(in net.Addr) (net.IP, bool) {
|
||||
switch addr := in.(type) {
|
||||
case *net.IPAddr:
|
||||
return addr.IP, true
|
||||
case *net.UDPAddr:
|
||||
return addr.IP, true
|
||||
case *net.TCPAddr:
|
||||
return addr.IP, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) {
|
||||
switch addr := in.(type) {
|
||||
case *net.UDPAddr:
|
||||
return addr.IP, addr.Port, NetworkTypeUDP4, true
|
||||
case *net.TCPAddr:
|
||||
return addr.IP, addr.Port, NetworkTypeTCP4, true
|
||||
}
|
||||
return nil, 0, 0, false
|
||||
}
|
||||
|
||||
func createAddr(network NetworkType, ip net.IP, port int) net.Addr {
|
||||
switch {
|
||||
case network.IsTCP():
|
||||
return &net.TCPAddr{IP: ip, Port: port}
|
||||
default:
|
||||
return &net.UDPAddr{IP: ip, Port: port}
|
||||
}
|
||||
}
|
||||
|
||||
func addrEqual(a, b net.Addr) bool {
|
||||
aIP, aPort, aType, aOk := parseAddr(a)
|
||||
if !aOk {
|
||||
return false
|
||||
}
|
||||
|
||||
bIP, bPort, bType, bOk := parseAddr(b)
|
||||
if !bOk {
|
||||
return false
|
||||
}
|
||||
|
||||
return aType == bType && aIP.Equal(bIP) && aPort == bPort
|
||||
}
|
||||
|
||||
// AddrPort is an IP and a port number.
|
||||
type AddrPort [18]byte
|
||||
|
||||
func toAddrPort(addr net.Addr) AddrPort {
|
||||
var ap AddrPort
|
||||
switch addr := addr.(type) {
|
||||
case *net.UDPAddr:
|
||||
copy(ap[:16], addr.IP.To16())
|
||||
ap[16] = uint8(addr.Port >> 8)
|
||||
ap[17] = uint8(addr.Port)
|
||||
case *net.TCPAddr:
|
||||
copy(ap[:16], addr.IP.To16())
|
||||
ap[16] = uint8(addr.Port >> 8)
|
||||
ap[17] = uint8(addr.Port)
|
||||
}
|
||||
return ap
|
||||
}
|
||||
1303
server/vendor/github.com/pion/ice/v2/agent.go
generated
vendored
Normal file
1303
server/vendor/github.com/pion/ice/v2/agent.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
305
server/vendor/github.com/pion/ice/v2/agent_config.go
generated
vendored
Normal file
305
server/vendor/github.com/pion/ice/v2/agent_config.go
generated
vendored
Normal file
@@ -0,0 +1,305 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase
|
||||
defaultCheckInterval = 200 * time.Millisecond
|
||||
|
||||
// keepaliveInterval used to keep candidates alive
|
||||
defaultKeepaliveInterval = 2 * time.Second
|
||||
|
||||
// defaultDisconnectedTimeout is the default time till an Agent transitions disconnected
|
||||
defaultDisconnectedTimeout = 5 * time.Second
|
||||
|
||||
// defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected
|
||||
defaultFailedTimeout = 25 * time.Second
|
||||
|
||||
// defaultHostAcceptanceMinWait is the wait time before nominating a host candidate
|
||||
defaultHostAcceptanceMinWait = 0
|
||||
|
||||
// defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate
|
||||
defaultSrflxAcceptanceMinWait = 500 * time.Millisecond
|
||||
|
||||
// defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate
|
||||
defaultPrflxAcceptanceMinWait = 1000 * time.Millisecond
|
||||
|
||||
// defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate
|
||||
defaultRelayAcceptanceMinWait = 2000 * time.Millisecond
|
||||
|
||||
// defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed
|
||||
defaultMaxBindingRequests = 7
|
||||
|
||||
// TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
|
||||
// for host, srflx and prfx candidate types.
|
||||
defaultTCPPriorityOffset = 27
|
||||
|
||||
// maxBufferSize is the number of bytes that can be buffered before we start to error
|
||||
maxBufferSize = 1000 * 1000 // 1MB
|
||||
|
||||
// maxBindingRequestTimeout is the wait time before binding requests can be deleted
|
||||
maxBindingRequestTimeout = 4000 * time.Millisecond
|
||||
)
|
||||
|
||||
func defaultCandidateTypes() []CandidateType {
|
||||
return []CandidateType{CandidateTypeHost, CandidateTypeServerReflexive, CandidateTypeRelay}
|
||||
}
|
||||
|
||||
// AgentConfig collects the arguments to ice.Agent construction into
|
||||
// a single structure, for future-proofness of the interface
|
||||
type AgentConfig struct {
|
||||
Urls []*stun.URI
|
||||
|
||||
// PortMin and PortMax are optional. Leave them 0 for the default UDP port allocation strategy.
|
||||
PortMin uint16
|
||||
PortMax uint16
|
||||
|
||||
// LocalUfrag and LocalPwd values used to perform connectivity
|
||||
// checks. The values MUST be unguessable, with at least 128 bits of
|
||||
// random number generator output used to generate the password, and
|
||||
// at least 24 bits of output to generate the username fragment.
|
||||
LocalUfrag string
|
||||
LocalPwd string
|
||||
|
||||
// MulticastDNSMode controls mDNS behavior for the ICE agent
|
||||
MulticastDNSMode MulticastDNSMode
|
||||
|
||||
// MulticastDNSHostName controls the hostname for this agent. If none is specified a random one will be generated
|
||||
MulticastDNSHostName string
|
||||
|
||||
// DisconnectedTimeout defaults to 5 seconds when this property is nil.
|
||||
// If the duration is 0, the ICE Agent will never go to disconnected
|
||||
DisconnectedTimeout *time.Duration
|
||||
|
||||
// FailedTimeout defaults to 25 seconds when this property is nil.
|
||||
// If the duration is 0, we will never go to failed.
|
||||
FailedTimeout *time.Duration
|
||||
|
||||
// KeepaliveInterval determines how often should we send ICE
|
||||
// keepalives (should be less then connectiontimeout above)
|
||||
// when this is nil, it defaults to 10 seconds.
|
||||
// A keepalive interval of 0 means we never send keepalive packets
|
||||
KeepaliveInterval *time.Duration
|
||||
|
||||
// CheckInterval controls how often our task loop runs when in the
|
||||
// connecting state.
|
||||
CheckInterval *time.Duration
|
||||
|
||||
// NetworkTypes is an optional configuration for disabling or enabling
|
||||
// support for specific network types.
|
||||
NetworkTypes []NetworkType
|
||||
|
||||
// CandidateTypes is an optional configuration for disabling or enabling
|
||||
// support for specific candidate types.
|
||||
CandidateTypes []CandidateType
|
||||
|
||||
LoggerFactory logging.LoggerFactory
|
||||
|
||||
// MaxBindingRequests is the max amount of binding requests the agent will send
|
||||
// over a candidate pair for validation or nomination, if after MaxBindingRequests
|
||||
// the candidate is yet to answer a binding request or a nomination we set the pair as failed
|
||||
MaxBindingRequests *uint16
|
||||
|
||||
// Lite agents do not perform connectivity check and only provide host candidates.
|
||||
Lite bool
|
||||
|
||||
// NAT1To1IPCandidateType is used along with NAT1To1IPs to specify which candidate type
|
||||
// the 1:1 NAT IP addresses should be mapped to.
|
||||
// If unspecified or CandidateTypeHost, NAT1To1IPs are used to replace host candidate IPs.
|
||||
// If CandidateTypeServerReflexive, it will insert a srflx candidate (as if it was derived
|
||||
// from a STUN server) with its port number being the one for the actual host candidate.
|
||||
// Other values will result in an error.
|
||||
NAT1To1IPCandidateType CandidateType
|
||||
|
||||
// NAT1To1IPs contains a list of public IP addresses that are to be used as a host
|
||||
// candidate or srflx candidate. This is used typically for servers that are behind
|
||||
// 1:1 D-NAT (e.g. AWS EC2 instances) and to eliminate the need of server reflexive
|
||||
// candidate gathering.
|
||||
NAT1To1IPs []string
|
||||
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting host candidates
|
||||
HostAcceptanceMinWait *time.Duration
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
|
||||
SrflxAcceptanceMinWait *time.Duration
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting prflx candidates
|
||||
PrflxAcceptanceMinWait *time.Duration
|
||||
// HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates
|
||||
RelayAcceptanceMinWait *time.Duration
|
||||
|
||||
// Net is the our abstracted network interface for internal development purpose only
|
||||
// (see https://github.com/pion/transport)
|
||||
Net transport.Net
|
||||
|
||||
// InterfaceFilter is a function that you can use in order to whitelist or blacklist
|
||||
// the interfaces which are used to gather ICE candidates.
|
||||
InterfaceFilter func(string) bool
|
||||
|
||||
// IPFilter is a function that you can use in order to whitelist or blacklist
|
||||
// the ips which are used to gather ICE candidates.
|
||||
IPFilter func(net.IP) bool
|
||||
|
||||
// InsecureSkipVerify controls if self-signed certificates are accepted when connecting
|
||||
// to TURN servers via TLS or DTLS
|
||||
InsecureSkipVerify bool
|
||||
|
||||
// TCPMux will be used for multiplexing incoming TCP connections for ICE TCP.
|
||||
// Currently only passive candidates are supported. This functionality is
|
||||
// experimental and the API might change in the future.
|
||||
TCPMux TCPMux
|
||||
|
||||
// UDPMux is used for multiplexing multiple incoming UDP connections on a single port
|
||||
// when this is set, the agent ignores PortMin and PortMax configurations and will
|
||||
// defer to UDPMux for incoming connections
|
||||
UDPMux UDPMux
|
||||
|
||||
// UDPMuxSrflx is used for multiplexing multiple incoming UDP connections of server reflexive candidates
|
||||
// on a single port when this is set, the agent ignores PortMin and PortMax configurations and will
|
||||
// defer to UDPMuxSrflx for incoming connections
|
||||
// It embeds UDPMux to do the actual connection multiplexing
|
||||
UDPMuxSrflx UniversalUDPMux
|
||||
|
||||
// Proxy Dialer is a dialer that should be implemented by the user based on golang.org/x/net/proxy
|
||||
// dial interface in order to support corporate proxies
|
||||
ProxyDialer proxy.Dialer
|
||||
|
||||
// Deprecated: AcceptAggressiveNomination always enabled.
|
||||
AcceptAggressiveNomination bool
|
||||
|
||||
// Include loopback addresses in the candidate list.
|
||||
IncludeLoopback bool
|
||||
|
||||
// TCPPriorityOffset is a number which is subtracted from the default (UDP) candidate type preference
|
||||
// for host, srflx and prfx candidate types. It helps to configure relative preference of UDP candidates
|
||||
// against TCP ones. Relay candidates for TCP and UDP are always 0 and not affected by this setting.
|
||||
// When this is nil, defaultTCPPriorityOffset is used.
|
||||
TCPPriorityOffset *uint16
|
||||
|
||||
// DisableActiveTCP can be used to disable Active TCP candidates. Otherwise when TCP is enabled
|
||||
// Active TCP candidates will be created when a new passive TCP remote candidate is added.
|
||||
DisableActiveTCP bool
|
||||
|
||||
// BindingRequestHandler allows applications to perform logic on incoming STUN Binding Requests
|
||||
// This was implemented to allow users to
|
||||
// * Log incoming Binding Requests for debugging
|
||||
// * Implement draft-thatcher-ice-renomination
|
||||
// * Implement custom CandidatePair switching logic
|
||||
BindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool
|
||||
}
|
||||
|
||||
// initWithDefaults populates an agent and falls back to defaults if fields are unset
|
||||
func (config *AgentConfig) initWithDefaults(a *Agent) {
|
||||
if config.MaxBindingRequests == nil {
|
||||
a.maxBindingRequests = defaultMaxBindingRequests
|
||||
} else {
|
||||
a.maxBindingRequests = *config.MaxBindingRequests
|
||||
}
|
||||
|
||||
if config.HostAcceptanceMinWait == nil {
|
||||
a.hostAcceptanceMinWait = defaultHostAcceptanceMinWait
|
||||
} else {
|
||||
a.hostAcceptanceMinWait = *config.HostAcceptanceMinWait
|
||||
}
|
||||
|
||||
if config.SrflxAcceptanceMinWait == nil {
|
||||
a.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait
|
||||
} else {
|
||||
a.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait
|
||||
}
|
||||
|
||||
if config.PrflxAcceptanceMinWait == nil {
|
||||
a.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait
|
||||
} else {
|
||||
a.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait
|
||||
}
|
||||
|
||||
if config.RelayAcceptanceMinWait == nil {
|
||||
a.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
|
||||
} else {
|
||||
a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
|
||||
}
|
||||
|
||||
if config.TCPPriorityOffset == nil {
|
||||
a.tcpPriorityOffset = defaultTCPPriorityOffset
|
||||
} else {
|
||||
a.tcpPriorityOffset = *config.TCPPriorityOffset
|
||||
}
|
||||
|
||||
if config.DisconnectedTimeout == nil {
|
||||
a.disconnectedTimeout = defaultDisconnectedTimeout
|
||||
} else {
|
||||
a.disconnectedTimeout = *config.DisconnectedTimeout
|
||||
}
|
||||
|
||||
if config.FailedTimeout == nil {
|
||||
a.failedTimeout = defaultFailedTimeout
|
||||
} else {
|
||||
a.failedTimeout = *config.FailedTimeout
|
||||
}
|
||||
|
||||
if config.KeepaliveInterval == nil {
|
||||
a.keepaliveInterval = defaultKeepaliveInterval
|
||||
} else {
|
||||
a.keepaliveInterval = *config.KeepaliveInterval
|
||||
}
|
||||
|
||||
if config.CheckInterval == nil {
|
||||
a.checkInterval = defaultCheckInterval
|
||||
} else {
|
||||
a.checkInterval = *config.CheckInterval
|
||||
}
|
||||
|
||||
if config.CandidateTypes == nil || len(config.CandidateTypes) == 0 {
|
||||
a.candidateTypes = defaultCandidateTypes()
|
||||
} else {
|
||||
a.candidateTypes = config.CandidateTypes
|
||||
}
|
||||
}
|
||||
|
||||
func (config *AgentConfig) initExtIPMapping(a *Agent) error {
|
||||
var err error
|
||||
a.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.extIPMapper == nil {
|
||||
return nil // This may happen when config.NAT1To1IPs is an empty array
|
||||
}
|
||||
if a.extIPMapper.candidateType == CandidateTypeHost {
|
||||
if a.mDNSMode == MulticastDNSModeQueryAndGather {
|
||||
return ErrMulticastDNSWithNAT1To1IPMapping
|
||||
}
|
||||
candiHostEnabled := false
|
||||
for _, candiType := range a.candidateTypes {
|
||||
if candiType == CandidateTypeHost {
|
||||
candiHostEnabled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !candiHostEnabled {
|
||||
return ErrIneffectiveNAT1To1IPMappingHost
|
||||
}
|
||||
} else if a.extIPMapper.candidateType == CandidateTypeServerReflexive {
|
||||
candiSrflxEnabled := false
|
||||
for _, candiType := range a.candidateTypes {
|
||||
if candiType == CandidateTypeServerReflexive {
|
||||
candiSrflxEnabled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !candiSrflxEnabled {
|
||||
return ErrIneffectiveNAT1To1IPMappingSrflx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
183
server/vendor/github.com/pion/ice/v2/agent_handlers.go
generated
vendored
Normal file
183
server/vendor/github.com/pion/ice/v2/agent_handlers.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "sync"
|
||||
|
||||
// OnConnectionStateChange sets a handler that is fired when the connection state changes
|
||||
func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error {
|
||||
a.onConnectionStateChangeHdlr.Store(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate
|
||||
// pair is selected
|
||||
func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error {
|
||||
a.onSelectedCandidatePairChangeHdlr.Store(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnCandidate sets a handler that is fired when new candidates gathered. When
|
||||
// the gathering process complete the last candidate is nil.
|
||||
func (a *Agent) OnCandidate(f func(Candidate)) error {
|
||||
a.onCandidateHdlr.Store(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) onSelectedCandidatePairChange(p *CandidatePair) {
|
||||
if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok {
|
||||
h(p.Local, p.Remote)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) onCandidate(c Candidate) {
|
||||
if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok {
|
||||
onCandidateHdlr(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) onConnectionStateChange(s ConnectionState) {
|
||||
if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok {
|
||||
hdlr(s)
|
||||
}
|
||||
}
|
||||
|
||||
type handlerNotifier struct {
|
||||
sync.Mutex
|
||||
running bool
|
||||
notifiers sync.WaitGroup
|
||||
|
||||
connectionStates []ConnectionState
|
||||
connectionStateFunc func(ConnectionState)
|
||||
|
||||
candidates []Candidate
|
||||
candidateFunc func(Candidate)
|
||||
|
||||
selectedCandidatePairs []*CandidatePair
|
||||
candidatePairFunc func(*CandidatePair)
|
||||
|
||||
// State for closing
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (h *handlerNotifier) Close(graceful bool) {
|
||||
if graceful {
|
||||
// if we were closed ungracefully before, we now
|
||||
// want ot wait.
|
||||
defer h.notifiers.Wait()
|
||||
}
|
||||
|
||||
h.Lock()
|
||||
|
||||
select {
|
||||
case <-h.done:
|
||||
h.Unlock()
|
||||
return
|
||||
default:
|
||||
}
|
||||
close(h.done)
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
func (h *handlerNotifier) EnqueueConnectionState(s ConnectionState) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
notify := func() {
|
||||
defer h.notifiers.Done()
|
||||
for {
|
||||
h.Lock()
|
||||
if len(h.connectionStates) == 0 {
|
||||
h.running = false
|
||||
h.Unlock()
|
||||
return
|
||||
}
|
||||
notification := h.connectionStates[0]
|
||||
h.connectionStates = h.connectionStates[1:]
|
||||
h.Unlock()
|
||||
h.connectionStateFunc(notification)
|
||||
}
|
||||
}
|
||||
|
||||
h.connectionStates = append(h.connectionStates, s)
|
||||
if !h.running {
|
||||
h.running = true
|
||||
h.notifiers.Add(1)
|
||||
go notify()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handlerNotifier) EnqueueCandidate(c Candidate) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
notify := func() {
|
||||
defer h.notifiers.Done()
|
||||
for {
|
||||
h.Lock()
|
||||
if len(h.candidates) == 0 {
|
||||
h.running = false
|
||||
h.Unlock()
|
||||
return
|
||||
}
|
||||
notification := h.candidates[0]
|
||||
h.candidates = h.candidates[1:]
|
||||
h.Unlock()
|
||||
h.candidateFunc(notification)
|
||||
}
|
||||
}
|
||||
|
||||
h.candidates = append(h.candidates, c)
|
||||
if !h.running {
|
||||
h.running = true
|
||||
h.notifiers.Add(1)
|
||||
go notify()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handlerNotifier) EnqueueSelectedCandidatePair(p *CandidatePair) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
notify := func() {
|
||||
defer h.notifiers.Done()
|
||||
for {
|
||||
h.Lock()
|
||||
if len(h.selectedCandidatePairs) == 0 {
|
||||
h.running = false
|
||||
h.Unlock()
|
||||
return
|
||||
}
|
||||
notification := h.selectedCandidatePairs[0]
|
||||
h.selectedCandidatePairs = h.selectedCandidatePairs[1:]
|
||||
h.Unlock()
|
||||
h.candidatePairFunc(notification)
|
||||
}
|
||||
}
|
||||
|
||||
h.selectedCandidatePairs = append(h.selectedCandidatePairs, p)
|
||||
if !h.running {
|
||||
h.running = true
|
||||
h.notifiers.Add(1)
|
||||
go notify()
|
||||
}
|
||||
}
|
||||
172
server/vendor/github.com/pion/ice/v2/agent_stats.go
generated
vendored
Normal file
172
server/vendor/github.com/pion/ice/v2/agent_stats.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetCandidatePairsStats returns a list of candidate pair stats
|
||||
func (a *Agent) GetCandidatePairsStats() []CandidatePairStats {
|
||||
var res []CandidatePairStats
|
||||
err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
|
||||
result := make([]CandidatePairStats, 0, len(agent.checklist))
|
||||
for _, cp := range agent.checklist {
|
||||
stat := CandidatePairStats{
|
||||
Timestamp: time.Now(),
|
||||
LocalCandidateID: cp.Local.ID(),
|
||||
RemoteCandidateID: cp.Remote.ID(),
|
||||
State: cp.state,
|
||||
Nominated: cp.nominated,
|
||||
// PacketsSent uint32
|
||||
// PacketsReceived uint32
|
||||
// BytesSent uint64
|
||||
// BytesReceived uint64
|
||||
// LastPacketSentTimestamp time.Time
|
||||
// LastPacketReceivedTimestamp time.Time
|
||||
// FirstRequestTimestamp time.Time
|
||||
// LastRequestTimestamp time.Time
|
||||
// LastResponseTimestamp time.Time
|
||||
TotalRoundTripTime: cp.TotalRoundTripTime(),
|
||||
CurrentRoundTripTime: cp.CurrentRoundTripTime(),
|
||||
// AvailableOutgoingBitrate float64
|
||||
// AvailableIncomingBitrate float64
|
||||
// CircuitBreakerTriggerCount uint32
|
||||
// RequestsReceived uint64
|
||||
// RequestsSent uint64
|
||||
ResponsesReceived: cp.ResponsesReceived(),
|
||||
// ResponsesSent uint64
|
||||
// RetransmissionsReceived uint64
|
||||
// RetransmissionsSent uint64
|
||||
// ConsentRequestsSent uint64
|
||||
// ConsentExpiredTimestamp time.Time
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
res = result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("Failed to get candidate pairs stats: %v", err)
|
||||
return []CandidatePairStats{}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetSelectedCandidatePairStats returns a candidate pair stats for selected candidate pair.
|
||||
// Returns false if there is no selected pair
|
||||
func (a *Agent) GetSelectedCandidatePairStats() (CandidatePairStats, bool) {
|
||||
isAvailable := false
|
||||
var res CandidatePairStats
|
||||
err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
|
||||
sp := agent.getSelectedPair()
|
||||
if sp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
isAvailable = true
|
||||
res = CandidatePairStats{
|
||||
Timestamp: time.Now(),
|
||||
LocalCandidateID: sp.Local.ID(),
|
||||
RemoteCandidateID: sp.Remote.ID(),
|
||||
State: sp.state,
|
||||
Nominated: sp.nominated,
|
||||
// PacketsSent uint32
|
||||
// PacketsReceived uint32
|
||||
// BytesSent uint64
|
||||
// BytesReceived uint64
|
||||
// LastPacketSentTimestamp time.Time
|
||||
// LastPacketReceivedTimestamp time.Time
|
||||
// FirstRequestTimestamp time.Time
|
||||
// LastRequestTimestamp time.Time
|
||||
// LastResponseTimestamp time.Time
|
||||
TotalRoundTripTime: sp.TotalRoundTripTime(),
|
||||
CurrentRoundTripTime: sp.CurrentRoundTripTime(),
|
||||
// AvailableOutgoingBitrate float64
|
||||
// AvailableIncomingBitrate float64
|
||||
// CircuitBreakerTriggerCount uint32
|
||||
// RequestsReceived uint64
|
||||
// RequestsSent uint64
|
||||
ResponsesReceived: sp.ResponsesReceived(),
|
||||
// ResponsesSent uint64
|
||||
// RetransmissionsReceived uint64
|
||||
// RetransmissionsSent uint64
|
||||
// ConsentRequestsSent uint64
|
||||
// ConsentExpiredTimestamp time.Time
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("Failed to get selected candidate pair stats: %v", err)
|
||||
return CandidatePairStats{}, false
|
||||
}
|
||||
|
||||
return res, isAvailable
|
||||
}
|
||||
|
||||
// GetLocalCandidatesStats returns a list of local candidates stats
|
||||
func (a *Agent) GetLocalCandidatesStats() []CandidateStats {
|
||||
var res []CandidateStats
|
||||
err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
|
||||
result := make([]CandidateStats, 0, len(agent.localCandidates))
|
||||
for networkType, localCandidates := range agent.localCandidates {
|
||||
for _, c := range localCandidates {
|
||||
relayProtocol := ""
|
||||
if c.Type() == CandidateTypeRelay {
|
||||
if cRelay, ok := c.(*CandidateRelay); ok {
|
||||
relayProtocol = cRelay.RelayProtocol()
|
||||
}
|
||||
}
|
||||
stat := CandidateStats{
|
||||
Timestamp: time.Now(),
|
||||
ID: c.ID(),
|
||||
NetworkType: networkType,
|
||||
IP: c.Address(),
|
||||
Port: c.Port(),
|
||||
CandidateType: c.Type(),
|
||||
Priority: c.Priority(),
|
||||
// URL string
|
||||
RelayProtocol: relayProtocol,
|
||||
// Deleted bool
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
}
|
||||
res = result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("Failed to get candidate pair stats: %v", err)
|
||||
return []CandidateStats{}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetRemoteCandidatesStats returns a list of remote candidates stats
|
||||
func (a *Agent) GetRemoteCandidatesStats() []CandidateStats {
|
||||
var res []CandidateStats
|
||||
err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
|
||||
result := make([]CandidateStats, 0, len(agent.remoteCandidates))
|
||||
for networkType, remoteCandidates := range agent.remoteCandidates {
|
||||
for _, c := range remoteCandidates {
|
||||
stat := CandidateStats{
|
||||
Timestamp: time.Now(),
|
||||
ID: c.ID(),
|
||||
NetworkType: networkType,
|
||||
IP: c.Address(),
|
||||
Port: c.Port(),
|
||||
CandidateType: c.Type(),
|
||||
Priority: c.Priority(),
|
||||
// URL string
|
||||
RelayProtocol: "",
|
||||
}
|
||||
result = append(result, stat)
|
||||
}
|
||||
}
|
||||
res = result
|
||||
})
|
||||
if err != nil {
|
||||
a.log.Errorf("Failed to get candidate pair stats: %v", err)
|
||||
return []CandidateStats{}
|
||||
}
|
||||
return res
|
||||
}
|
||||
72
server/vendor/github.com/pion/ice/v2/candidate.go
generated
vendored
Normal file
72
server/vendor/github.com/pion/ice/v2/candidate.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
receiveMTU = 8192
|
||||
defaultLocalPreference = 65535
|
||||
|
||||
// ComponentRTP indicates that the candidate is used for RTP
|
||||
ComponentRTP uint16 = 1
|
||||
// ComponentRTCP indicates that the candidate is used for RTCP
|
||||
ComponentRTCP
|
||||
)
|
||||
|
||||
// Candidate represents an ICE candidate
|
||||
type Candidate interface {
|
||||
// An arbitrary string used in the freezing algorithm to
|
||||
// group similar candidates. It is the same for two candidates that
|
||||
// have the same type, base IP address, protocol (UDP, TCP, etc.),
|
||||
// and STUN or TURN server.
|
||||
Foundation() string
|
||||
|
||||
// ID is a unique identifier for just this candidate
|
||||
// Unlike the foundation this is different for each candidate
|
||||
ID() string
|
||||
|
||||
// A component is a piece of a data stream.
|
||||
// An example is one for RTP, and one for RTCP
|
||||
Component() uint16
|
||||
SetComponent(uint16)
|
||||
|
||||
// The last time this candidate received traffic
|
||||
LastReceived() time.Time
|
||||
|
||||
// The last time this candidate sent traffic
|
||||
LastSent() time.Time
|
||||
|
||||
NetworkType() NetworkType
|
||||
Address() string
|
||||
Port() int
|
||||
|
||||
Priority() uint32
|
||||
|
||||
// A transport address related to a
|
||||
// candidate, which is useful for diagnostics and other purposes
|
||||
RelatedAddress() *CandidateRelatedAddress
|
||||
|
||||
String() string
|
||||
Type() CandidateType
|
||||
TCPType() TCPType
|
||||
|
||||
Equal(other Candidate) bool
|
||||
|
||||
Marshal() string
|
||||
|
||||
addr() net.Addr
|
||||
agent() *Agent
|
||||
context() context.Context
|
||||
|
||||
close() error
|
||||
copy() (Candidate, error)
|
||||
seen(outbound bool)
|
||||
start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{})
|
||||
writeTo(raw []byte, dst Candidate) (int, error)
|
||||
}
|
||||
572
server/vendor/github.com/pion/ice/v2/candidate_base.go
generated
vendored
Normal file
572
server/vendor/github.com/pion/ice/v2/candidate_base.go
generated
vendored
Normal file
@@ -0,0 +1,572 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
type candidateBase struct {
|
||||
id string
|
||||
networkType NetworkType
|
||||
candidateType CandidateType
|
||||
|
||||
component uint16
|
||||
address string
|
||||
port int
|
||||
relatedAddress *CandidateRelatedAddress
|
||||
tcpType TCPType
|
||||
|
||||
resolvedAddr net.Addr
|
||||
|
||||
lastSent atomic.Value
|
||||
lastReceived atomic.Value
|
||||
conn net.PacketConn
|
||||
|
||||
currAgent *Agent
|
||||
closeCh chan struct{}
|
||||
closedCh chan struct{}
|
||||
|
||||
foundationOverride string
|
||||
priorityOverride uint32
|
||||
|
||||
remoteCandidateCaches map[AddrPort]Candidate
|
||||
}
|
||||
|
||||
// Done implements context.Context
|
||||
func (c *candidateBase) Done() <-chan struct{} {
|
||||
return c.closeCh
|
||||
}
|
||||
|
||||
// Err implements context.Context
|
||||
func (c *candidateBase) Err() error {
|
||||
select {
|
||||
case <-c.closedCh:
|
||||
return ErrRunCanceled
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Deadline implements context.Context
|
||||
func (c *candidateBase) Deadline() (deadline time.Time, ok bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// Value implements context.Context
|
||||
func (c *candidateBase) Value(interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns Candidate ID
|
||||
func (c *candidateBase) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *candidateBase) Foundation() string {
|
||||
if c.foundationOverride != "" {
|
||||
return c.foundationOverride
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(c.Type().String()+c.address+c.networkType.String())))
|
||||
}
|
||||
|
||||
// Address returns Candidate Address
|
||||
func (c *candidateBase) Address() string {
|
||||
return c.address
|
||||
}
|
||||
|
||||
// Port returns Candidate Port
|
||||
func (c *candidateBase) Port() int {
|
||||
return c.port
|
||||
}
|
||||
|
||||
// Type returns candidate type
|
||||
func (c *candidateBase) Type() CandidateType {
|
||||
return c.candidateType
|
||||
}
|
||||
|
||||
// NetworkType returns candidate NetworkType
|
||||
func (c *candidateBase) NetworkType() NetworkType {
|
||||
return c.networkType
|
||||
}
|
||||
|
||||
// Component returns candidate component
|
||||
func (c *candidateBase) Component() uint16 {
|
||||
return c.component
|
||||
}
|
||||
|
||||
func (c *candidateBase) SetComponent(component uint16) {
|
||||
c.component = component
|
||||
}
|
||||
|
||||
// LocalPreference returns the local preference for this candidate
|
||||
func (c *candidateBase) LocalPreference() uint16 {
|
||||
if c.NetworkType().IsTCP() {
|
||||
// RFC 6544, section 4.2
|
||||
//
|
||||
// In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE
|
||||
// candidate prioritization is defined. For TCP candidates, the same
|
||||
// formula and candidate type preferences SHOULD be used, and the
|
||||
// RECOMMENDED type preferences for the new candidate types defined in
|
||||
// this document (see Section 5) are 105 for NAT-assisted candidates and
|
||||
// 75 for UDP-tunneled candidates.
|
||||
//
|
||||
// (...)
|
||||
//
|
||||
// With TCP candidates, the local preference part of the recommended
|
||||
// priority formula is updated to also include the directionality
|
||||
// (active, passive, or simultaneous-open) of the TCP connection. The
|
||||
// RECOMMENDED local preference is then defined as:
|
||||
//
|
||||
// local preference = (2^13) * direction-pref + other-pref
|
||||
//
|
||||
// The direction-pref MUST be between 0 and 7 (both inclusive), with 7
|
||||
// being the most preferred. The other-pref MUST be between 0 and 8191
|
||||
// (both inclusive), with 8191 being the most preferred. It is
|
||||
// RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates
|
||||
// have the direction-pref assigned as follows: 6 for active, 4 for
|
||||
// passive, and 2 for S-O. For the NAT-assisted and server reflexive
|
||||
// candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and
|
||||
// 2 for passive.
|
||||
//
|
||||
// (...)
|
||||
//
|
||||
// If any two candidates have the same type-preference and direction-
|
||||
// pref, they MUST have a unique other-pref. With this specification,
|
||||
// this usually only happens with multi-homed hosts, in which case
|
||||
// other-pref is the preference for the particular IP address from which
|
||||
// the candidate was obtained. When there is only a single IP address,
|
||||
// this value SHOULD be set to the maximum allowed value (8191).
|
||||
var otherPref uint16 = 8191
|
||||
|
||||
directionPref := func() uint16 {
|
||||
switch c.Type() {
|
||||
case CandidateTypeHost, CandidateTypeRelay:
|
||||
switch c.tcpType {
|
||||
case TCPTypeActive:
|
||||
return 6
|
||||
case TCPTypePassive:
|
||||
return 4
|
||||
case TCPTypeSimultaneousOpen:
|
||||
return 2
|
||||
case TCPTypeUnspecified:
|
||||
return 0
|
||||
}
|
||||
case CandidateTypePeerReflexive, CandidateTypeServerReflexive:
|
||||
switch c.tcpType {
|
||||
case TCPTypeSimultaneousOpen:
|
||||
return 6
|
||||
case TCPTypeActive:
|
||||
return 4
|
||||
case TCPTypePassive:
|
||||
return 2
|
||||
case TCPTypeUnspecified:
|
||||
return 0
|
||||
}
|
||||
case CandidateTypeUnspecified:
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}()
|
||||
|
||||
return (1<<13)*directionPref + otherPref
|
||||
}
|
||||
|
||||
return defaultLocalPreference
|
||||
}
|
||||
|
||||
// RelatedAddress returns *CandidateRelatedAddress
|
||||
func (c *candidateBase) RelatedAddress() *CandidateRelatedAddress {
|
||||
return c.relatedAddress
|
||||
}
|
||||
|
||||
func (c *candidateBase) TCPType() TCPType {
|
||||
return c.tcpType
|
||||
}
|
||||
|
||||
// start runs the candidate using the provided connection
|
||||
func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{}) {
|
||||
if c.conn != nil {
|
||||
c.agent().log.Warn("Can't start already started candidateBase")
|
||||
return
|
||||
}
|
||||
c.currAgent = a
|
||||
c.conn = conn
|
||||
c.closeCh = make(chan struct{})
|
||||
c.closedCh = make(chan struct{})
|
||||
|
||||
go c.recvLoop(initializedCh)
|
||||
}
|
||||
|
||||
func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) {
|
||||
a := c.agent()
|
||||
|
||||
defer close(c.closedCh)
|
||||
|
||||
select {
|
||||
case <-initializedCh:
|
||||
case <-c.closeCh:
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, srcAddr, err := c.conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
if !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) {
|
||||
a.log.Warnf("Failed to read from candidate %s: %v", c, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.handleInboundPacket(buf[:n], srcAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *candidateBase) validateSTUNTrafficCache(addr net.Addr) bool {
|
||||
if candidate, ok := c.remoteCandidateCaches[toAddrPort(addr)]; ok {
|
||||
candidate.seen(false)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *candidateBase) addRemoteCandidateCache(candidate Candidate, srcAddr net.Addr) {
|
||||
if c.validateSTUNTrafficCache(srcAddr) {
|
||||
return
|
||||
}
|
||||
c.remoteCandidateCaches[toAddrPort(srcAddr)] = candidate
|
||||
}
|
||||
|
||||
func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) {
|
||||
a := c.agent()
|
||||
|
||||
if stun.IsMessage(buf) {
|
||||
m := &stun.Message{
|
||||
Raw: make([]byte, len(buf)),
|
||||
}
|
||||
|
||||
// Explicitly copy raw buffer so Message can own the memory.
|
||||
copy(m.Raw, buf)
|
||||
|
||||
if err := m.Decode(); err != nil {
|
||||
a.log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.run(c, func(ctx context.Context, a *Agent) {
|
||||
// nolint: contextcheck
|
||||
a.handleInbound(m, c, srcAddr)
|
||||
}); err != nil {
|
||||
a.log.Warnf("Failed to handle message: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !c.validateSTUNTrafficCache(srcAddr) {
|
||||
remoteCandidate, valid := a.validateNonSTUNTraffic(c, srcAddr) //nolint:contextcheck
|
||||
if !valid {
|
||||
a.log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr())
|
||||
return
|
||||
}
|
||||
c.addRemoteCandidateCache(remoteCandidate, srcAddr)
|
||||
}
|
||||
|
||||
// Note: This will return packetio.ErrFull if the buffer ever manages to fill up.
|
||||
if _, err := a.buf.Write(buf); err != nil {
|
||||
a.log.Warnf("Failed to write packet: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// close stops the recvLoop
|
||||
func (c *candidateBase) close() error {
|
||||
// If conn has never been started will be nil
|
||||
if c.Done() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assert that conn has not already been closed
|
||||
select {
|
||||
case <-c.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
|
||||
// Unblock recvLoop
|
||||
close(c.closeCh)
|
||||
if err := c.conn.SetDeadline(time.Now()); err != nil {
|
||||
firstErr = err
|
||||
}
|
||||
|
||||
// Close the conn
|
||||
if err := c.conn.Close(); err != nil && firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
|
||||
if firstErr != nil {
|
||||
return firstErr
|
||||
}
|
||||
|
||||
// Wait until the recvLoop is closed
|
||||
<-c.closedCh
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) {
|
||||
n, err := c.conn.WriteTo(raw, dst.addr())
|
||||
if err != nil {
|
||||
// If the connection is closed, we should return the error
|
||||
if errors.Is(err, io.ErrClosedPipe) {
|
||||
return n, err
|
||||
}
|
||||
c.agent().log.Infof("Failed to send packet: %v", err)
|
||||
return n, nil
|
||||
}
|
||||
c.seen(true)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// TypePreference returns the type preference for this candidate
|
||||
func (c *candidateBase) TypePreference() uint16 {
|
||||
pref := c.Type().Preference()
|
||||
if pref == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if c.NetworkType().IsTCP() {
|
||||
var tcpPriorityOffset uint16 = defaultTCPPriorityOffset
|
||||
if c.agent() != nil {
|
||||
tcpPriorityOffset = c.agent().tcpPriorityOffset
|
||||
}
|
||||
|
||||
pref -= tcpPriorityOffset
|
||||
}
|
||||
|
||||
return pref
|
||||
}
|
||||
|
||||
// Priority computes the priority for this ICE Candidate
|
||||
// See: https://www.rfc-editor.org/rfc/rfc8445#section-5.1.2.1
|
||||
func (c *candidateBase) Priority() uint32 {
|
||||
if c.priorityOverride != 0 {
|
||||
return c.priorityOverride
|
||||
}
|
||||
|
||||
// The local preference MUST be an integer from 0 (lowest preference) to
|
||||
// 65535 (highest preference) inclusive. When there is only a single IP
|
||||
// address, this value SHOULD be set to 65535. If there are multiple
|
||||
// candidates for a particular component for a particular data stream
|
||||
// that have the same type, the local preference MUST be unique for each
|
||||
// one.
|
||||
|
||||
return (1<<24)*uint32(c.TypePreference()) +
|
||||
(1<<8)*uint32(c.LocalPreference()) +
|
||||
(1<<0)*uint32(256-c.Component())
|
||||
}
|
||||
|
||||
// Equal is used to compare two candidateBases
|
||||
func (c *candidateBase) Equal(other Candidate) bool {
|
||||
return c.NetworkType() == other.NetworkType() &&
|
||||
c.Type() == other.Type() &&
|
||||
c.Address() == other.Address() &&
|
||||
c.Port() == other.Port() &&
|
||||
c.TCPType() == other.TCPType() &&
|
||||
c.RelatedAddress().Equal(other.RelatedAddress())
|
||||
}
|
||||
|
||||
// String makes the candidateBase printable
|
||||
func (c *candidateBase) String() string {
|
||||
return fmt.Sprintf("%s %s %s%s", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress)
|
||||
}
|
||||
|
||||
// LastReceived returns a time.Time indicating the last time
|
||||
// this candidate was received
|
||||
func (c *candidateBase) LastReceived() time.Time {
|
||||
if lastReceived, ok := c.lastReceived.Load().(time.Time); ok {
|
||||
return lastReceived
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (c *candidateBase) setLastReceived(t time.Time) {
|
||||
c.lastReceived.Store(t)
|
||||
}
|
||||
|
||||
// LastSent returns a time.Time indicating the last time
|
||||
// this candidate was sent
|
||||
func (c *candidateBase) LastSent() time.Time {
|
||||
if lastSent, ok := c.lastSent.Load().(time.Time); ok {
|
||||
return lastSent
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (c *candidateBase) setLastSent(t time.Time) {
|
||||
c.lastSent.Store(t)
|
||||
}
|
||||
|
||||
func (c *candidateBase) seen(outbound bool) {
|
||||
if outbound {
|
||||
c.setLastSent(time.Now())
|
||||
} else {
|
||||
c.setLastReceived(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *candidateBase) addr() net.Addr {
|
||||
return c.resolvedAddr
|
||||
}
|
||||
|
||||
func (c *candidateBase) agent() *Agent {
|
||||
return c.currAgent
|
||||
}
|
||||
|
||||
func (c *candidateBase) context() context.Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *candidateBase) copy() (Candidate, error) {
|
||||
return UnmarshalCandidate(c.Marshal())
|
||||
}
|
||||
|
||||
func removeZoneIDFromAddress(addr string) string {
|
||||
if i := strings.Index(addr, "%"); i != -1 {
|
||||
return addr[:i]
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// Marshal returns the string representation of the ICECandidate
|
||||
func (c *candidateBase) Marshal() string {
|
||||
val := c.Foundation()
|
||||
if val == " " {
|
||||
val = ""
|
||||
}
|
||||
|
||||
val = fmt.Sprintf("%s %d %s %d %s %d typ %s",
|
||||
val,
|
||||
c.Component(),
|
||||
c.NetworkType().NetworkShort(),
|
||||
c.Priority(),
|
||||
removeZoneIDFromAddress(c.Address()),
|
||||
c.Port(),
|
||||
c.Type())
|
||||
|
||||
if c.tcpType != TCPTypeUnspecified {
|
||||
val += fmt.Sprintf(" tcptype %s", c.tcpType.String())
|
||||
}
|
||||
|
||||
if r := c.RelatedAddress(); r != nil && r.Address != "" && r.Port != 0 {
|
||||
val = fmt.Sprintf("%s raddr %s rport %d",
|
||||
val,
|
||||
r.Address,
|
||||
r.Port)
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// UnmarshalCandidate creates a Candidate from its string representation
|
||||
func UnmarshalCandidate(raw string) (Candidate, error) {
|
||||
split := strings.Fields(raw)
|
||||
// Foundation not specified: not RFC 8445 compliant but seen in the wild
|
||||
if len(raw) != 0 && raw[0] == ' ' {
|
||||
split = append([]string{" "}, split...)
|
||||
}
|
||||
if len(split) < 8 {
|
||||
return nil, fmt.Errorf("%w (%d)", errAttributeTooShortICECandidate, len(split))
|
||||
}
|
||||
|
||||
// Foundation
|
||||
foundation := split[0]
|
||||
|
||||
// Component
|
||||
rawComponent, err := strconv.ParseUint(split[1], 10, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errParseComponent, err) //nolint:errorlint
|
||||
}
|
||||
component := uint16(rawComponent)
|
||||
|
||||
// Protocol
|
||||
protocol := split[2]
|
||||
|
||||
// Priority
|
||||
priorityRaw, err := strconv.ParseUint(split[3], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errParsePriority, err) //nolint:errorlint
|
||||
}
|
||||
priority := uint32(priorityRaw)
|
||||
|
||||
// Address
|
||||
address := removeZoneIDFromAddress(split[4])
|
||||
|
||||
// Port
|
||||
rawPort, err := strconv.ParseUint(split[5], 10, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errParsePort, err) //nolint:errorlint
|
||||
}
|
||||
port := int(rawPort)
|
||||
typ := split[7]
|
||||
|
||||
relatedAddress := ""
|
||||
relatedPort := 0
|
||||
tcpType := TCPTypeUnspecified
|
||||
|
||||
if len(split) > 8 {
|
||||
split = split[8:]
|
||||
|
||||
if split[0] == "raddr" {
|
||||
if len(split) < 4 {
|
||||
return nil, fmt.Errorf("%w: incorrect length", errParseRelatedAddr)
|
||||
}
|
||||
|
||||
// RelatedAddress
|
||||
relatedAddress = split[1]
|
||||
|
||||
// RelatedPort
|
||||
rawRelatedPort, parseErr := strconv.ParseUint(split[3], 10, 16)
|
||||
if parseErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errParsePort, parseErr) //nolint:errorlint
|
||||
}
|
||||
relatedPort = int(rawRelatedPort)
|
||||
} else if split[0] == "tcptype" {
|
||||
if len(split) < 2 {
|
||||
return nil, fmt.Errorf("%w: incorrect length", errParseTCPType)
|
||||
}
|
||||
|
||||
tcpType = NewTCPType(split[1])
|
||||
}
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "host":
|
||||
return NewCandidateHost(&CandidateHostConfig{"", protocol, address, port, component, priority, foundation, tcpType})
|
||||
case "srflx":
|
||||
return NewCandidateServerReflexive(&CandidateServerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort})
|
||||
case "prflx":
|
||||
return NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort})
|
||||
case "relay":
|
||||
return NewCandidateRelay(&CandidateRelayConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort, "", nil})
|
||||
default:
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w (%s)", ErrUnknownCandidateTyp, typ)
|
||||
}
|
||||
80
server/vendor/github.com/pion/ice/v2/candidate_host.go
generated
vendored
Normal file
80
server/vendor/github.com/pion/ice/v2/candidate_host.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CandidateHost is a candidate of type host
|
||||
type CandidateHost struct {
|
||||
candidateBase
|
||||
|
||||
network string
|
||||
}
|
||||
|
||||
// CandidateHostConfig is the config required to create a new CandidateHost
|
||||
type CandidateHostConfig struct {
|
||||
CandidateID string
|
||||
Network string
|
||||
Address string
|
||||
Port int
|
||||
Component uint16
|
||||
Priority uint32
|
||||
Foundation string
|
||||
TCPType TCPType
|
||||
}
|
||||
|
||||
// NewCandidateHost creates a new host candidate
|
||||
func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) {
|
||||
candidateID := config.CandidateID
|
||||
|
||||
if candidateID == "" {
|
||||
candidateID = globalCandidateIDGenerator.Generate()
|
||||
}
|
||||
|
||||
c := &CandidateHost{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
address: config.Address,
|
||||
candidateType: CandidateTypeHost,
|
||||
component: config.Component,
|
||||
port: config.Port,
|
||||
tcpType: config.TCPType,
|
||||
foundationOverride: config.Foundation,
|
||||
priorityOverride: config.Priority,
|
||||
remoteCandidateCaches: map[AddrPort]Candidate{},
|
||||
},
|
||||
network: config.Network,
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(config.Address, ".local") {
|
||||
ip := net.ParseIP(config.Address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
if err := c.setIP(ip); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Until mDNS candidate is resolved assume it is UDPv4
|
||||
c.candidateBase.networkType = NetworkTypeUDP4
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *CandidateHost) setIP(ip net.IP) error {
|
||||
networkType, err := determineNetworkType(c.network, ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.candidateBase.networkType = networkType
|
||||
c.candidateBase.resolvedAddr = createAddr(networkType, ip, c.port)
|
||||
|
||||
return nil
|
||||
}
|
||||
64
server/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
generated
vendored
Normal file
64
server/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package ice ...
|
||||
//
|
||||
//nolint:dupl
|
||||
package ice
|
||||
|
||||
import "net"
|
||||
|
||||
// CandidatePeerReflexive ...
|
||||
type CandidatePeerReflexive struct {
|
||||
candidateBase
|
||||
}
|
||||
|
||||
// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive
|
||||
type CandidatePeerReflexiveConfig struct {
|
||||
CandidateID string
|
||||
Network string
|
||||
Address string
|
||||
Port int
|
||||
Component uint16
|
||||
Priority uint32
|
||||
Foundation string
|
||||
RelAddr string
|
||||
RelPort int
|
||||
}
|
||||
|
||||
// NewCandidatePeerReflexive creates a new peer reflective candidate
|
||||
func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*CandidatePeerReflexive, error) {
|
||||
ip := net.ParseIP(config.Address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(config.Network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidateID := config.CandidateID
|
||||
if candidateID == "" {
|
||||
candidateID = globalCandidateIDGenerator.Generate()
|
||||
}
|
||||
|
||||
return &CandidatePeerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypePeerReflexive,
|
||||
address: config.Address,
|
||||
port: config.Port,
|
||||
resolvedAddr: createAddr(networkType, ip, config.Port),
|
||||
component: config.Component,
|
||||
foundationOverride: config.Foundation,
|
||||
priorityOverride: config.Priority,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: config.RelAddr,
|
||||
Port: config.RelPort,
|
||||
},
|
||||
remoteCandidateCaches: map[AddrPort]Candidate{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
115
server/vendor/github.com/pion/ice/v2/candidate_relay.go
generated
vendored
Normal file
115
server/vendor/github.com/pion/ice/v2/candidate_relay.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// CandidateRelay ...
|
||||
type CandidateRelay struct {
|
||||
candidateBase
|
||||
|
||||
relayProtocol string
|
||||
onClose func() error
|
||||
}
|
||||
|
||||
// CandidateRelayConfig is the config required to create a new CandidateRelay
|
||||
type CandidateRelayConfig struct {
|
||||
CandidateID string
|
||||
Network string
|
||||
Address string
|
||||
Port int
|
||||
Component uint16
|
||||
Priority uint32
|
||||
Foundation string
|
||||
RelAddr string
|
||||
RelPort int
|
||||
RelayProtocol string
|
||||
OnClose func() error
|
||||
}
|
||||
|
||||
// NewCandidateRelay creates a new relay candidate
|
||||
func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) {
|
||||
candidateID := config.CandidateID
|
||||
|
||||
if candidateID == "" {
|
||||
candidateID = globalCandidateIDGenerator.Generate()
|
||||
}
|
||||
|
||||
ip := net.ParseIP(config.Address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(config.Network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CandidateRelay{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeRelay,
|
||||
address: config.Address,
|
||||
port: config.Port,
|
||||
resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
|
||||
component: config.Component,
|
||||
foundationOverride: config.Foundation,
|
||||
priorityOverride: config.Priority,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: config.RelAddr,
|
||||
Port: config.RelPort,
|
||||
},
|
||||
remoteCandidateCaches: map[AddrPort]Candidate{},
|
||||
},
|
||||
relayProtocol: config.RelayProtocol,
|
||||
onClose: config.OnClose,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LocalPreference returns the local preference for this candidate
|
||||
func (c *CandidateRelay) LocalPreference() uint16 {
|
||||
// These preference values come from libwebrtc
|
||||
// https://github.com/mozilla/libwebrtc/blob/1389c76d9c79839a2ca069df1db48aa3f2e6a1ac/p2p/base/turn_port.cc#L61
|
||||
var relayPreference uint16
|
||||
switch c.relayProtocol {
|
||||
case relayProtocolTLS, relayProtocolDTLS:
|
||||
relayPreference = 2
|
||||
case tcp:
|
||||
relayPreference = 1
|
||||
default:
|
||||
relayPreference = 0
|
||||
}
|
||||
|
||||
return c.candidateBase.LocalPreference() + relayPreference
|
||||
}
|
||||
|
||||
// RelayProtocol returns the protocol used between the endpoint and the relay server.
|
||||
func (c *CandidateRelay) RelayProtocol() string {
|
||||
return c.relayProtocol
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) close() error {
|
||||
err := c.candidateBase.close()
|
||||
if c.onClose != nil {
|
||||
err = c.onClose()
|
||||
c.onClose = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CandidateRelay) copy() (Candidate, error) {
|
||||
cc, err := c.candidateBase.copy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ccr, ok := cc.(*CandidateRelay); ok {
|
||||
ccr.relayProtocol = c.relayProtocol
|
||||
}
|
||||
|
||||
return cc, nil
|
||||
}
|
||||
61
server/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
generated
vendored
Normal file
61
server/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "net"
|
||||
|
||||
// CandidateServerReflexive ...
|
||||
type CandidateServerReflexive struct {
|
||||
candidateBase
|
||||
}
|
||||
|
||||
// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive
|
||||
type CandidateServerReflexiveConfig struct {
|
||||
CandidateID string
|
||||
Network string
|
||||
Address string
|
||||
Port int
|
||||
Component uint16
|
||||
Priority uint32
|
||||
Foundation string
|
||||
RelAddr string
|
||||
RelPort int
|
||||
}
|
||||
|
||||
// NewCandidateServerReflexive creates a new server reflective candidate
|
||||
func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*CandidateServerReflexive, error) {
|
||||
ip := net.ParseIP(config.Address)
|
||||
if ip == nil {
|
||||
return nil, ErrAddressParseFailed
|
||||
}
|
||||
|
||||
networkType, err := determineNetworkType(config.Network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidateID := config.CandidateID
|
||||
if candidateID == "" {
|
||||
candidateID = globalCandidateIDGenerator.Generate()
|
||||
}
|
||||
|
||||
return &CandidateServerReflexive{
|
||||
candidateBase: candidateBase{
|
||||
id: candidateID,
|
||||
networkType: networkType,
|
||||
candidateType: CandidateTypeServerReflexive,
|
||||
address: config.Address,
|
||||
port: config.Port,
|
||||
resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
|
||||
component: config.Component,
|
||||
foundationOverride: config.Foundation,
|
||||
priorityOverride: config.Priority,
|
||||
relatedAddress: &CandidateRelatedAddress{
|
||||
Address: config.RelAddr,
|
||||
Port: config.RelPort,
|
||||
},
|
||||
remoteCandidateCaches: map[AddrPort]Candidate{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
136
server/vendor/github.com/pion/ice/v2/candidatepair.go
generated
vendored
Normal file
136
server/vendor/github.com/pion/ice/v2/candidatepair.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
func newCandidatePair(local, remote Candidate, controlling bool) *CandidatePair {
|
||||
return &CandidatePair{
|
||||
iceRoleControlling: controlling,
|
||||
Remote: remote,
|
||||
Local: local,
|
||||
state: CandidatePairStateWaiting,
|
||||
}
|
||||
}
|
||||
|
||||
// CandidatePair is a combination of a
|
||||
// local and remote candidate
|
||||
type CandidatePair struct {
|
||||
iceRoleControlling bool
|
||||
Remote Candidate
|
||||
Local Candidate
|
||||
bindingRequestCount uint16
|
||||
state CandidatePairState
|
||||
nominated bool
|
||||
nominateOnBindingSuccess bool
|
||||
|
||||
// stats
|
||||
currentRoundTripTime int64 // in ns
|
||||
totalRoundTripTime int64 // in ns
|
||||
responsesReceived uint64
|
||||
}
|
||||
|
||||
func (p *CandidatePair) String() string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("prio %d (local, prio %d) %s <-> %s (remote, prio %d), state: %s, nominated: %v, nominateOnBindingSuccess: %v",
|
||||
p.priority(), p.Local.Priority(), p.Local, p.Remote, p.Remote.Priority(), p.state, p.nominated, p.nominateOnBindingSuccess)
|
||||
}
|
||||
|
||||
func (p *CandidatePair) equal(other *CandidatePair) bool {
|
||||
if p == nil && other == nil {
|
||||
return true
|
||||
}
|
||||
if p == nil || other == nil {
|
||||
return false
|
||||
}
|
||||
return p.Local.Equal(other.Local) && p.Remote.Equal(other.Remote)
|
||||
}
|
||||
|
||||
// RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
|
||||
// Let G be the priority for the candidate provided by the controlling
|
||||
// agent. Let D be the priority for the candidate provided by the
|
||||
// controlled agent.
|
||||
// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
|
||||
func (p *CandidatePair) priority() uint64 {
|
||||
var g, d uint32
|
||||
if p.iceRoleControlling {
|
||||
g = p.Local.Priority()
|
||||
d = p.Remote.Priority()
|
||||
} else {
|
||||
g = p.Remote.Priority()
|
||||
d = p.Local.Priority()
|
||||
}
|
||||
|
||||
// Just implement these here rather
|
||||
// than fooling around with the math package
|
||||
min := func(x, y uint32) uint64 {
|
||||
if x < y {
|
||||
return uint64(x)
|
||||
}
|
||||
return uint64(y)
|
||||
}
|
||||
max := func(x, y uint32) uint64 {
|
||||
if x > y {
|
||||
return uint64(x)
|
||||
}
|
||||
return uint64(y)
|
||||
}
|
||||
cmp := func(x, y uint32) uint64 {
|
||||
if x > y {
|
||||
return uint64(1)
|
||||
}
|
||||
return uint64(0)
|
||||
}
|
||||
|
||||
// 1<<32 overflows uint32; and if both g && d are
|
||||
// maxUint32, this result would overflow uint64
|
||||
return (1<<32-1)*min(g, d) + 2*max(g, d) + cmp(g, d)
|
||||
}
|
||||
|
||||
func (p *CandidatePair) Write(b []byte) (int, error) {
|
||||
return p.Local.writeTo(b, p.Remote)
|
||||
}
|
||||
|
||||
func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) {
|
||||
_, err := local.writeTo(msg.Raw, remote)
|
||||
if err != nil {
|
||||
a.log.Tracef("Failed to send STUN message: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRoundTripTime sets the current round time of this pair and
|
||||
// accumulates total round trip time and responses received
|
||||
func (p *CandidatePair) UpdateRoundTripTime(rtt time.Duration) {
|
||||
rttNs := rtt.Nanoseconds()
|
||||
atomic.StoreInt64(&p.currentRoundTripTime, rttNs)
|
||||
atomic.AddInt64(&p.totalRoundTripTime, rttNs)
|
||||
atomic.AddUint64(&p.responsesReceived, 1)
|
||||
}
|
||||
|
||||
// CurrentRoundTripTime returns the current round trip time in seconds
|
||||
// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
|
||||
func (p *CandidatePair) CurrentRoundTripTime() float64 {
|
||||
return time.Duration(atomic.LoadInt64(&p.currentRoundTripTime)).Seconds()
|
||||
}
|
||||
|
||||
// TotalRoundTripTime returns the current round trip time in seconds
|
||||
// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
|
||||
func (p *CandidatePair) TotalRoundTripTime() float64 {
|
||||
return time.Duration(atomic.LoadInt64(&p.totalRoundTripTime)).Seconds()
|
||||
}
|
||||
|
||||
// ResponsesReceived returns the total number of connectivity responses received
|
||||
// https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-responsesreceived
|
||||
func (p *CandidatePair) ResponsesReceived() uint64 {
|
||||
return atomic.LoadUint64(&p.responsesReceived)
|
||||
}
|
||||
40
server/vendor/github.com/pion/ice/v2/candidatepair_state.go
generated
vendored
Normal file
40
server/vendor/github.com/pion/ice/v2/candidatepair_state.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
// CandidatePairState represent the ICE candidate pair state
|
||||
type CandidatePairState int
|
||||
|
||||
const (
|
||||
// CandidatePairStateWaiting means a check has not been performed for
|
||||
// this pair
|
||||
CandidatePairStateWaiting = iota + 1
|
||||
|
||||
// CandidatePairStateInProgress means a check has been sent for this pair,
|
||||
// but the transaction is in progress.
|
||||
CandidatePairStateInProgress
|
||||
|
||||
// CandidatePairStateFailed means a check for this pair was already done
|
||||
// and failed, either never producing any response or producing an unrecoverable
|
||||
// failure response.
|
||||
CandidatePairStateFailed
|
||||
|
||||
// CandidatePairStateSucceeded means a check for this pair was already
|
||||
// done and produced a successful result.
|
||||
CandidatePairStateSucceeded
|
||||
)
|
||||
|
||||
func (c CandidatePairState) String() string {
|
||||
switch c {
|
||||
case CandidatePairStateWaiting:
|
||||
return "waiting"
|
||||
case CandidatePairStateInProgress:
|
||||
return "in-progress"
|
||||
case CandidatePairStateFailed:
|
||||
return "failed"
|
||||
case CandidatePairStateSucceeded:
|
||||
return "succeeded"
|
||||
}
|
||||
return "Unknown candidate pair state"
|
||||
}
|
||||
33
server/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
generated
vendored
Normal file
33
server/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CandidateRelatedAddress convey transport addresses related to the
|
||||
// candidate, useful for diagnostics and other purposes.
|
||||
type CandidateRelatedAddress struct {
|
||||
Address string
|
||||
Port int
|
||||
}
|
||||
|
||||
// String makes CandidateRelatedAddress printable
|
||||
func (c *CandidateRelatedAddress) String() string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" related %s:%d", c.Address, c.Port)
|
||||
}
|
||||
|
||||
// Equal allows comparing two CandidateRelatedAddresses.
|
||||
// The CandidateRelatedAddress are allowed to be nil.
|
||||
func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool {
|
||||
if c == nil && other == nil {
|
||||
return true
|
||||
}
|
||||
return c != nil && other != nil &&
|
||||
c.Address == other.Address &&
|
||||
c.Port == other.Port
|
||||
}
|
||||
65
server/vendor/github.com/pion/ice/v2/candidatetype.go
generated
vendored
Normal file
65
server/vendor/github.com/pion/ice/v2/candidatetype.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
// CandidateType represents the type of candidate
|
||||
type CandidateType byte
|
||||
|
||||
// CandidateType enum
|
||||
const (
|
||||
CandidateTypeUnspecified CandidateType = iota
|
||||
CandidateTypeHost
|
||||
CandidateTypeServerReflexive
|
||||
CandidateTypePeerReflexive
|
||||
CandidateTypeRelay
|
||||
)
|
||||
|
||||
// String makes CandidateType printable
|
||||
func (c CandidateType) String() string {
|
||||
switch c {
|
||||
case CandidateTypeHost:
|
||||
return "host"
|
||||
case CandidateTypeServerReflexive:
|
||||
return "srflx"
|
||||
case CandidateTypePeerReflexive:
|
||||
return "prflx"
|
||||
case CandidateTypeRelay:
|
||||
return "relay"
|
||||
case CandidateTypeUnspecified:
|
||||
return "Unknown candidate type"
|
||||
}
|
||||
return "Unknown candidate type"
|
||||
}
|
||||
|
||||
// Preference returns the preference weight of a CandidateType
|
||||
//
|
||||
// 4.1.2.2. Guidelines for Choosing Type and Local Preferences
|
||||
// The RECOMMENDED values are 126 for host candidates, 100
|
||||
// for server reflexive candidates, 110 for peer reflexive candidates,
|
||||
// and 0 for relayed candidates.
|
||||
func (c CandidateType) Preference() uint16 {
|
||||
switch c {
|
||||
case CandidateTypeHost:
|
||||
return 126
|
||||
case CandidateTypePeerReflexive:
|
||||
return 110
|
||||
case CandidateTypeServerReflexive:
|
||||
return 100
|
||||
case CandidateTypeRelay, CandidateTypeUnspecified:
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func containsCandidateType(candidateType CandidateType, candidateTypeList []CandidateType) bool {
|
||||
if candidateTypeList == nil {
|
||||
return false
|
||||
}
|
||||
for _, ct := range candidateTypeList {
|
||||
if ct == candidateType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
22
server/vendor/github.com/pion/ice/v2/codecov.yml
generated
vendored
Normal file
22
server/vendor/github.com/pion/ice/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/**/*"
|
||||
40
server/vendor/github.com/pion/ice/v2/context.go
generated
vendored
Normal file
40
server/vendor/github.com/pion/ice/v2/context.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (a *Agent) context() context.Context {
|
||||
return agentContext(a.done)
|
||||
}
|
||||
|
||||
type agentContext chan struct{}
|
||||
|
||||
// Done implements context.Context
|
||||
func (a agentContext) Done() <-chan struct{} {
|
||||
return (chan struct{})(a)
|
||||
}
|
||||
|
||||
// Err implements context.Context
|
||||
func (a agentContext) Err() error {
|
||||
select {
|
||||
case <-(chan struct{})(a):
|
||||
return ErrRunCanceled
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Deadline implements context.Context
|
||||
func (a agentContext) Deadline() (deadline time.Time, ok bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// Value implements context.Context
|
||||
func (a agentContext) Value(interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
140
server/vendor/github.com/pion/ice/v2/errors.go
generated
vendored
Normal file
140
server/vendor/github.com/pion/ice/v2/errors.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrUnknownType indicates an error with Unknown info.
|
||||
ErrUnknownType = errors.New("Unknown")
|
||||
|
||||
// ErrSchemeType indicates the scheme type could not be parsed.
|
||||
ErrSchemeType = errors.New("unknown scheme type")
|
||||
|
||||
// ErrSTUNQuery indicates query arguments are provided in a STUN URL.
|
||||
ErrSTUNQuery = errors.New("queries not supported in STUN address")
|
||||
|
||||
// ErrInvalidQuery indicates an malformed query is provided.
|
||||
ErrInvalidQuery = errors.New("invalid query")
|
||||
|
||||
// ErrHost indicates malformed hostname is provided.
|
||||
ErrHost = errors.New("invalid hostname")
|
||||
|
||||
// ErrPort indicates malformed port is provided.
|
||||
ErrPort = errors.New("invalid port")
|
||||
|
||||
// ErrLocalUfragInsufficientBits indicates local username fragment insufficient bits are provided.
|
||||
// Have to be at least 24 bits long
|
||||
ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long")
|
||||
|
||||
// ErrLocalPwdInsufficientBits indicates local password insufficient bits are provided.
|
||||
// Have to be at least 128 bits long
|
||||
ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long")
|
||||
|
||||
// ErrProtoType indicates an unsupported transport type was provided.
|
||||
ErrProtoType = errors.New("invalid transport protocol type")
|
||||
|
||||
// ErrClosed indicates the agent is closed
|
||||
ErrClosed = errors.New("the agent is closed")
|
||||
|
||||
// ErrNoCandidatePairs indicates agent does not have a valid candidate pair
|
||||
ErrNoCandidatePairs = errors.New("no candidate pairs available")
|
||||
|
||||
// ErrCanceledByCaller indicates agent connection was canceled by the caller
|
||||
ErrCanceledByCaller = errors.New("connecting canceled by caller")
|
||||
|
||||
// ErrMultipleStart indicates agent was started twice
|
||||
ErrMultipleStart = errors.New("attempted to start agent twice")
|
||||
|
||||
// ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag
|
||||
ErrRemoteUfragEmpty = errors.New("remote ufrag is empty")
|
||||
|
||||
// ErrRemotePwdEmpty indicates agent was started with an empty remote pwd
|
||||
ErrRemotePwdEmpty = errors.New("remote pwd is empty")
|
||||
|
||||
// ErrNoOnCandidateHandler indicates agent was started without OnCandidate
|
||||
ErrNoOnCandidateHandler = errors.New("no OnCandidate provided")
|
||||
|
||||
// ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times
|
||||
ErrMultipleGatherAttempted = errors.New("attempting to gather candidates during gathering state")
|
||||
|
||||
// ErrUsernameEmpty indicates agent was give TURN URL with an empty Username
|
||||
ErrUsernameEmpty = errors.New("username is empty")
|
||||
|
||||
// ErrPasswordEmpty indicates agent was give TURN URL with an empty Password
|
||||
ErrPasswordEmpty = errors.New("password is empty")
|
||||
|
||||
// ErrAddressParseFailed indicates we were unable to parse a candidate address
|
||||
ErrAddressParseFailed = errors.New("failed to parse address")
|
||||
|
||||
// ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent
|
||||
ErrLiteUsingNonHostCandidates = errors.New("lite agents must only use host candidates")
|
||||
|
||||
// ErrUselessUrlsProvided indicates that one or more URL was provided to the agent but no host
|
||||
// candidate required them
|
||||
ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types")
|
||||
|
||||
// ErrUnsupportedNAT1To1IPCandidateType indicates that the specified NAT1To1IPCandidateType is
|
||||
// unsupported
|
||||
ErrUnsupportedNAT1To1IPCandidateType = errors.New("unsupported 1:1 NAT IP candidate type")
|
||||
|
||||
// ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid
|
||||
ErrInvalidNAT1To1IPMapping = errors.New("invalid 1:1 NAT IP mapping")
|
||||
|
||||
// ErrExternalMappedIPNotFound in NAT1To1IPMapping
|
||||
ErrExternalMappedIPNotFound = errors.New("external mapped IP not found")
|
||||
|
||||
// ErrMulticastDNSWithNAT1To1IPMapping indicates that the mDNS gathering cannot be used along
|
||||
// with 1:1 NAT IP mapping for host candidate.
|
||||
ErrMulticastDNSWithNAT1To1IPMapping = errors.New("mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate")
|
||||
|
||||
// ErrIneffectiveNAT1To1IPMappingHost indicates that 1:1 NAT IP mapping for host candidate is
|
||||
// requested, but the host candidate type is disabled.
|
||||
ErrIneffectiveNAT1To1IPMappingHost = errors.New("1:1 NAT IP mapping for host candidate ineffective")
|
||||
|
||||
// ErrIneffectiveNAT1To1IPMappingSrflx indicates that 1:1 NAT IP mapping for srflx candidate is
|
||||
// requested, but the srflx candidate type is disabled.
|
||||
ErrIneffectiveNAT1To1IPMappingSrflx = errors.New("1:1 NAT IP mapping for srflx candidate ineffective")
|
||||
|
||||
// ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName
|
||||
ErrInvalidMulticastDNSHostName = errors.New("invalid mDNS HostName, must end with .local and can only contain a single '.'")
|
||||
|
||||
// ErrRunCanceled indicates a run operation was canceled by its individual done
|
||||
ErrRunCanceled = errors.New("run was canceled by done")
|
||||
|
||||
// ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr.
|
||||
ErrTCPRemoteAddrAlreadyExists = errors.New("conn with same remote addr already exists")
|
||||
|
||||
// ErrUnknownCandidateTyp indicates that a candidate had a unknown type value.
|
||||
ErrUnknownCandidateTyp = errors.New("unknown candidate typ")
|
||||
|
||||
// ErrDetermineNetworkType indicates that the NetworkType was not able to be parsed
|
||||
ErrDetermineNetworkType = errors.New("unable to determine networkType")
|
||||
|
||||
errAttributeTooShortICECandidate = errors.New("attribute not long enough to be ICE candidate")
|
||||
errClosingConnection = errors.New("failed to close connection")
|
||||
errConnectionAddrAlreadyExist = errors.New("connection with same remote address already exists")
|
||||
errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response")
|
||||
errInvalidAddress = errors.New("invalid address")
|
||||
errNoTCPMuxAvailable = errors.New("no TCP mux is available")
|
||||
errNotImplemented = errors.New("not implemented yet")
|
||||
errNoUDPMuxAvailable = errors.New("no UDP mux is available")
|
||||
errNoXorAddrMapping = errors.New("no address mapping")
|
||||
errParseComponent = errors.New("failed to parse component")
|
||||
errParsePort = errors.New("failed to parse port")
|
||||
errParsePriority = errors.New("failed to parse priority")
|
||||
errParseRelatedAddr = errors.New("failed to parse related addresses")
|
||||
errParseTCPType = errors.New("failed to parse TCP type")
|
||||
errRead = errors.New("failed to read")
|
||||
errUDPMuxDisabled = errors.New("UDPMux is not enabled")
|
||||
errUnknownRole = errors.New("unknown role")
|
||||
errWrite = errors.New("failed to write")
|
||||
errWriteSTUNMessage = errors.New("failed to send STUN message")
|
||||
errWriteSTUNMessageToIceConn = errors.New("failed to write STUN message to ICE connection")
|
||||
errXORMappedAddrTimeout = errors.New("timeout while waiting for XORMappedAddr")
|
||||
|
||||
// UDPMuxDefault should not listen on unspecified address, but to keep backward compatibility, don't return error now.
|
||||
// will be used in the future.
|
||||
// errListenUnspecified = errors.New("can't listen on unspecified address")
|
||||
)
|
||||
153
server/vendor/github.com/pion/ice/v2/external_ip_mapper.go
generated
vendored
Normal file
153
server/vendor/github.com/pion/ice/v2/external_ip_mapper.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func validateIPString(ipStr string) (net.IP, bool, error) {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return nil, false, ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
return ip, (ip.To4() != nil), nil
|
||||
}
|
||||
|
||||
// ipMapping holds the mapping of local and external IP address for a particular IP family
|
||||
type ipMapping struct {
|
||||
ipSole net.IP // When non-nil, this is the sole external IP for one local IP assumed
|
||||
ipMap map[string]net.IP // Local-to-external IP mapping (k: local, v: external)
|
||||
valid bool // If not set any external IP, valid is false
|
||||
}
|
||||
|
||||
func (m *ipMapping) setSoleIP(ip net.IP) error {
|
||||
if m.ipSole != nil || len(m.ipMap) > 0 {
|
||||
return ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
m.ipSole = ip
|
||||
m.valid = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ipMapping) addIPMapping(locIP, extIP net.IP) error {
|
||||
if m.ipSole != nil {
|
||||
return ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
locIPStr := locIP.String()
|
||||
|
||||
// Check if dup of local IP
|
||||
if _, ok := m.ipMap[locIPStr]; ok {
|
||||
return ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
m.ipMap[locIPStr] = extIP
|
||||
m.valid = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ipMapping) findExternalIP(locIP net.IP) (net.IP, error) {
|
||||
if !m.valid {
|
||||
return locIP, nil
|
||||
}
|
||||
|
||||
if m.ipSole != nil {
|
||||
return m.ipSole, nil
|
||||
}
|
||||
|
||||
extIP, ok := m.ipMap[locIP.String()]
|
||||
if !ok {
|
||||
return nil, ErrExternalMappedIPNotFound
|
||||
}
|
||||
|
||||
return extIP, nil
|
||||
}
|
||||
|
||||
type externalIPMapper struct {
|
||||
ipv4Mapping ipMapping
|
||||
ipv6Mapping ipMapping
|
||||
candidateType CandidateType
|
||||
}
|
||||
|
||||
func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit
|
||||
if len(ips) == 0 {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
if candidateType == CandidateTypeUnspecified {
|
||||
candidateType = CandidateTypeHost // Defaults to host
|
||||
} else if candidateType != CandidateTypeHost && candidateType != CandidateTypeServerReflexive {
|
||||
return nil, ErrUnsupportedNAT1To1IPCandidateType
|
||||
}
|
||||
|
||||
m := &externalIPMapper{
|
||||
ipv4Mapping: ipMapping{ipMap: map[string]net.IP{}},
|
||||
ipv6Mapping: ipMapping{ipMap: map[string]net.IP{}},
|
||||
candidateType: candidateType,
|
||||
}
|
||||
|
||||
for _, extIPStr := range ips {
|
||||
ipPair := strings.Split(extIPStr, "/")
|
||||
if len(ipPair) == 0 || len(ipPair) > 2 {
|
||||
return nil, ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
extIP, isExtIPv4, err := validateIPString(ipPair[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ipPair) == 1 {
|
||||
if isExtIPv4 {
|
||||
if err := m.ipv4Mapping.setSoleIP(extIP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := m.ipv6Mapping.setSoleIP(extIP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
locIP, isLocIPv4, err := validateIPString(ipPair[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isExtIPv4 {
|
||||
if !isLocIPv4 {
|
||||
return nil, ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
if err := m.ipv4Mapping.addIPMapping(locIP, extIP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if isLocIPv4 {
|
||||
return nil, ErrInvalidNAT1To1IPMapping
|
||||
}
|
||||
|
||||
if err := m.ipv6Mapping.addIPMapping(locIP, extIP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *externalIPMapper) findExternalIP(localIPStr string) (net.IP, error) {
|
||||
locIP, isLocIPv4, err := validateIPString(localIPStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isLocIPv4 {
|
||||
return m.ipv4Mapping.findExternalIP(locIP)
|
||||
}
|
||||
|
||||
return m.ipv6Mapping.findExternalIP(locIP)
|
||||
}
|
||||
720
server/vendor/github.com/pion/ice/v2/gather.go
generated
vendored
Normal file
720
server/vendor/github.com/pion/ice/v2/gather.go
generated
vendored
Normal file
@@ -0,0 +1,720 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2"
|
||||
"github.com/pion/ice/v2/internal/fakenet"
|
||||
stunx "github.com/pion/ice/v2/internal/stun"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/turn/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
stunGatherTimeout = time.Second * 5
|
||||
)
|
||||
|
||||
// Close a net.Conn and log if we have a failure
|
||||
func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) {
|
||||
if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) {
|
||||
log.Warnf("Connection is not allocated: "+msg, args...)
|
||||
return
|
||||
}
|
||||
|
||||
log.Warnf(msg, args...)
|
||||
if err := c.Close(); err != nil {
|
||||
log.Warnf("Failed to close connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GatherCandidates initiates the trickle based gathering process.
|
||||
func (a *Agent) GatherCandidates() error {
|
||||
var gatherErr error
|
||||
|
||||
if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
|
||||
if a.gatheringState != GatheringStateNew {
|
||||
gatherErr = ErrMultipleGatherAttempted
|
||||
return
|
||||
} else if a.onCandidateHdlr.Load() == nil {
|
||||
gatherErr = ErrNoOnCandidateHandler
|
||||
return
|
||||
}
|
||||
|
||||
a.gatherCandidateCancel() // Cancel previous gathering routine
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
a.gatherCandidateCancel = cancel
|
||||
done := make(chan struct{})
|
||||
a.gatherCandidateDone = done
|
||||
|
||||
go a.gatherCandidates(ctx, done)
|
||||
}); runErr != nil {
|
||||
return runErr
|
||||
}
|
||||
return gatherErr
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) {
|
||||
defer close(done)
|
||||
if err := a.setGatheringState(GatheringStateGathering); err != nil { //nolint:contextcheck
|
||||
a.log.Warnf("Failed to set gatheringState to GatheringStateGathering: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, t := range a.candidateTypes {
|
||||
switch t {
|
||||
case CandidateTypeHost:
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
a.gatherCandidatesLocal(ctx, a.networkTypes)
|
||||
wg.Done()
|
||||
}()
|
||||
case CandidateTypeServerReflexive:
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if a.udpMuxSrflx != nil {
|
||||
a.gatherCandidatesSrflxUDPMux(ctx, a.urls, a.networkTypes)
|
||||
} else {
|
||||
a.gatherCandidatesSrflx(ctx, a.urls, a.networkTypes)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeServerReflexive {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
a.gatherCandidatesSrflxMapped(ctx, a.networkTypes)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
case CandidateTypeRelay:
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
a.gatherCandidatesRelay(ctx, a.urls)
|
||||
wg.Done()
|
||||
}()
|
||||
case CandidateTypePeerReflexive, CandidateTypeUnspecified:
|
||||
}
|
||||
}
|
||||
|
||||
// Block until all STUN and TURN URLs have been gathered (or timed out)
|
||||
wg.Wait()
|
||||
|
||||
if err := a.setGatheringState(GatheringStateComplete); err != nil { //nolint:contextcheck
|
||||
a.log.Warnf("Failed to set gatheringState to GatheringStateComplete: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) { //nolint:gocognit
|
||||
networks := map[string]struct{}{}
|
||||
for _, networkType := range networkTypes {
|
||||
if networkType.IsTCP() {
|
||||
networks[tcp] = struct{}{}
|
||||
} else {
|
||||
networks[udp] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// When UDPMux is enabled, skip other UDP candidates
|
||||
if a.udpMux != nil {
|
||||
if err := a.gatherCandidatesLocalUDPMux(ctx); err != nil {
|
||||
a.log.Warnf("Failed to create host candidate for UDPMux: %s", err)
|
||||
}
|
||||
delete(networks, udp)
|
||||
}
|
||||
|
||||
localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, ip := range localIPs {
|
||||
mappedIP := ip
|
||||
if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
|
||||
if _mappedIP, innerErr := a.extIPMapper.findExternalIP(ip.String()); innerErr == nil {
|
||||
mappedIP = _mappedIP
|
||||
} else {
|
||||
a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", ip.String())
|
||||
}
|
||||
}
|
||||
|
||||
address := mappedIP.String()
|
||||
if a.mDNSMode == MulticastDNSModeQueryAndGather {
|
||||
address = a.mDNSName
|
||||
}
|
||||
|
||||
for network := range networks {
|
||||
type connAndPort struct {
|
||||
conn net.PacketConn
|
||||
port int
|
||||
}
|
||||
var (
|
||||
conns []connAndPort
|
||||
tcpType TCPType
|
||||
)
|
||||
|
||||
switch network {
|
||||
case tcp:
|
||||
if a.tcpMux == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle ICE TCP passive mode
|
||||
var muxConns []net.PacketConn
|
||||
if multi, ok := a.tcpMux.(AllConnsGetter); ok {
|
||||
a.log.Debugf("GetAllConns by ufrag: %s", a.localUfrag)
|
||||
muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.To4() == nil, ip)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
a.log.Debugf("GetConn by ufrag: %s", a.localUfrag)
|
||||
conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.To4() == nil, ip)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag)
|
||||
continue
|
||||
}
|
||||
muxConns = []net.PacketConn{conn}
|
||||
}
|
||||
|
||||
// Extract the port for each PacketConn we got.
|
||||
for _, conn := range muxConns {
|
||||
if tcpConn, ok := conn.LocalAddr().(*net.TCPAddr); ok {
|
||||
conns = append(conns, connAndPort{conn, tcpConn.Port})
|
||||
} else {
|
||||
a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, ip, a.localUfrag)
|
||||
}
|
||||
}
|
||||
if len(conns) == 0 {
|
||||
// Didn't succeed with any, try the next network.
|
||||
continue
|
||||
}
|
||||
tcpType = TCPTypePassive
|
||||
// Is there a way to verify that the listen address is even
|
||||
// accessible from the current interface.
|
||||
case udp:
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: ip, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to listen %s %s", network, ip)
|
||||
continue
|
||||
}
|
||||
|
||||
if udpConn, ok := conn.LocalAddr().(*net.UDPAddr); ok {
|
||||
conns = append(conns, connAndPort{conn, udpConn.Port})
|
||||
} else {
|
||||
a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, ip, a.localUfrag)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
for _, connAndPort := range conns {
|
||||
hostConfig := CandidateHostConfig{
|
||||
Network: network,
|
||||
Address: address,
|
||||
Port: connAndPort.port,
|
||||
Component: ComponentRTP,
|
||||
TCPType: tcpType,
|
||||
}
|
||||
|
||||
c, err := NewCandidateHost(&hostConfig)
|
||||
if err != nil {
|
||||
closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if a.mDNSMode == MulticastDNSModeQueryAndGather {
|
||||
if err = c.setIP(ip); err != nil {
|
||||
closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, c, connAndPort.conn); err != nil {
|
||||
if closeErr := c.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit
|
||||
if a.udpMux == nil {
|
||||
return errUDPMuxDisabled
|
||||
}
|
||||
|
||||
localAddresses := a.udpMux.GetListenAddresses()
|
||||
existingConfigs := make(map[CandidateHostConfig]struct{})
|
||||
|
||||
for _, addr := range localAddresses {
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return errInvalidAddress
|
||||
}
|
||||
candidateIP := udpAddr.IP
|
||||
if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
|
||||
mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String())
|
||||
if err != nil {
|
||||
a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", candidateIP.String())
|
||||
continue
|
||||
}
|
||||
|
||||
candidateIP = mappedIP
|
||||
}
|
||||
|
||||
hostConfig := CandidateHostConfig{
|
||||
Network: udp,
|
||||
Address: candidateIP.String(),
|
||||
Port: udpAddr.Port,
|
||||
Component: ComponentRTP,
|
||||
}
|
||||
|
||||
// Detect a duplicate candidate before calling addCandidate().
|
||||
// otherwise, addCandidate() detects the duplicate candidate
|
||||
// and close its connection, invalidating all candidates
|
||||
// that share the same connection.
|
||||
if _, ok := existingConfigs[hostConfig]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
conn, err := a.udpMux.GetConn(a.localUfrag, udpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := NewCandidateHost(&hostConfig)
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to create host mux candidate: %s %d: %v", candidateIP, udpAddr.Port, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, c, conn); err != nil {
|
||||
if closeErr := c.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
|
||||
closeConnAndLog(conn, a.log, "failed to add candidate: %s %d: %v", candidateIP, udpAddr.Port, err)
|
||||
continue
|
||||
}
|
||||
|
||||
existingConfigs[hostConfig] = struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes []NetworkType) {
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
for _, networkType := range networkTypes {
|
||||
if networkType.IsTCP() {
|
||||
continue
|
||||
}
|
||||
|
||||
network := networkType.String()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to listen %s: %v", network, err)
|
||||
return
|
||||
}
|
||||
|
||||
lAddr, ok := conn.LocalAddr().(*net.UDPAddr)
|
||||
if !ok {
|
||||
closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but LocalAddr is not a UDPAddr")
|
||||
return
|
||||
}
|
||||
|
||||
mappedIP, err := a.extIPMapper.findExternalIP(lAddr.IP.String())
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but no external IP is found for %s", lAddr.IP.String())
|
||||
return
|
||||
}
|
||||
|
||||
srflxConfig := CandidateServerReflexiveConfig{
|
||||
Network: network,
|
||||
Address: mappedIP.String(),
|
||||
Port: lAddr.Port,
|
||||
Component: ComponentRTP,
|
||||
RelAddr: lAddr.IP.String(),
|
||||
RelPort: lAddr.Port,
|
||||
}
|
||||
c, err := NewCandidateServerReflexive(&srflxConfig)
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v",
|
||||
network,
|
||||
mappedIP.String(),
|
||||
lAddr.Port,
|
||||
err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, c, conn); err != nil {
|
||||
if closeErr := c.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
for _, networkType := range networkTypes {
|
||||
if networkType.IsTCP() {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range urls {
|
||||
for _, listenAddr := range a.udpMuxSrflx.GetListenAddresses() {
|
||||
udpAddr, ok := listenAddr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
a.log.Warn("Failed to cast udpMuxSrflx listen address to UDPAddr")
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(url stun.URI, network string, localAddr *net.UDPAddr) {
|
||||
defer wg.Done()
|
||||
|
||||
hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
|
||||
serverAddr, err := a.net.ResolveUDPAddr(network, hostPort)
|
||||
if err != nil {
|
||||
a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err)
|
||||
return
|
||||
}
|
||||
|
||||
xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, stunGatherTimeout)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := a.udpMuxSrflx.GetConnForURL(a.localUfrag, url.String(), localAddr)
|
||||
if err != nil {
|
||||
a.log.Warnf("Failed to find connection in UDPMuxSrflx %s %s: %v", network, url, err)
|
||||
return
|
||||
}
|
||||
|
||||
ip := xorAddr.IP
|
||||
port := xorAddr.Port
|
||||
|
||||
srflxConfig := CandidateServerReflexiveConfig{
|
||||
Network: network,
|
||||
Address: ip.String(),
|
||||
Port: port,
|
||||
Component: ComponentRTP,
|
||||
RelAddr: localAddr.IP.String(),
|
||||
RelPort: localAddr.Port,
|
||||
}
|
||||
c, err := NewCandidateServerReflexive(&srflxConfig)
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, c, conn); err != nil {
|
||||
if closeErr := c.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
|
||||
}
|
||||
}(*urls[i], networkType.String(), udpAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
for _, networkType := range networkTypes {
|
||||
if networkType.IsTCP() {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range urls {
|
||||
wg.Add(1)
|
||||
go func(url stun.URI, network string) {
|
||||
defer wg.Done()
|
||||
|
||||
hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
|
||||
serverAddr, err := a.net.ResolveUDPAddr(network, hostPort)
|
||||
if err != nil {
|
||||
a.log.Debugf("Failed to resolve STUN host: %s %s: %v", network, hostPort, err)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0})
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to listen for %s: %v", serverAddr.String(), err)
|
||||
return
|
||||
}
|
||||
// If the agent closes midway through the connection
|
||||
// we end it early to prevent close delay.
|
||||
cancelCtx, cancelFunc := context.WithCancel(ctx)
|
||||
defer cancelFunc()
|
||||
go func() {
|
||||
select {
|
||||
case <-cancelCtx.Done():
|
||||
return
|
||||
case <-a.done:
|
||||
_ = conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, stunGatherTimeout)
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to get server reflexive address %s %s: %v", network, url, err)
|
||||
return
|
||||
}
|
||||
|
||||
ip := xorAddr.IP
|
||||
port := xorAddr.Port
|
||||
|
||||
lAddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
srflxConfig := CandidateServerReflexiveConfig{
|
||||
Network: network,
|
||||
Address: ip.String(),
|
||||
Port: port,
|
||||
Component: ComponentRTP,
|
||||
RelAddr: lAddr.IP.String(),
|
||||
RelPort: lAddr.Port,
|
||||
}
|
||||
c, err := NewCandidateServerReflexive(&srflxConfig)
|
||||
if err != nil {
|
||||
closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, c, conn); err != nil {
|
||||
if closeErr := c.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
|
||||
}
|
||||
}(*urls[i], networkType.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { //nolint:gocognit
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
network := NetworkTypeUDP4.String()
|
||||
for i := range urls {
|
||||
switch {
|
||||
case urls[i].Scheme != stun.SchemeTypeTURN && urls[i].Scheme != stun.SchemeTypeTURNS:
|
||||
continue
|
||||
case urls[i].Username == "":
|
||||
a.log.Errorf("Failed to gather relay candidates: %v", ErrUsernameEmpty)
|
||||
return
|
||||
case urls[i].Password == "":
|
||||
a.log.Errorf("Failed to gather relay candidates: %v", ErrPasswordEmpty)
|
||||
return
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(url stun.URI) {
|
||||
defer wg.Done()
|
||||
turnServerAddr := fmt.Sprintf("%s:%d", url.Host, url.Port)
|
||||
var (
|
||||
locConn net.PacketConn
|
||||
err error
|
||||
relAddr string
|
||||
relPort int
|
||||
relayProtocol string
|
||||
)
|
||||
|
||||
switch {
|
||||
case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURN:
|
||||
if locConn, err = a.net.ListenPacket(network, "0.0.0.0:0"); err != nil {
|
||||
a.log.Warnf("Failed to listen %s: %v", network, err)
|
||||
return
|
||||
}
|
||||
|
||||
relAddr = locConn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert
|
||||
relPort = locConn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert
|
||||
relayProtocol = udp
|
||||
case a.proxyDialer != nil && url.Proto == stun.ProtoTypeTCP &&
|
||||
(url.Scheme == stun.SchemeTypeTURN || url.Scheme == stun.SchemeTypeTURNS):
|
||||
conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), turnServerAddr)
|
||||
if connectErr != nil {
|
||||
a.log.Warnf("Failed to dial TCP address %s via proxy dialer: %v", turnServerAddr, connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert
|
||||
relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert
|
||||
if url.Scheme == stun.SchemeTypeTURN {
|
||||
relayProtocol = tcp
|
||||
} else if url.Scheme == stun.SchemeTypeTURNS {
|
||||
relayProtocol = "tls"
|
||||
}
|
||||
locConn = turn.NewSTUNConn(conn)
|
||||
|
||||
case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURN:
|
||||
tcpAddr, connectErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr)
|
||||
if connectErr != nil {
|
||||
a.log.Warnf("Failed to resolve TCP address %s: %v", turnServerAddr, connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
conn, connectErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr)
|
||||
if connectErr != nil {
|
||||
a.log.Warnf("Failed to dial TCP address %s: %v", turnServerAddr, connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert
|
||||
relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert
|
||||
relayProtocol = tcp
|
||||
locConn = turn.NewSTUNConn(conn)
|
||||
case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURNS:
|
||||
udpAddr, connectErr := a.net.ResolveUDPAddr(network, turnServerAddr)
|
||||
if connectErr != nil {
|
||||
a.log.Warnf("Failed to resolve UDP address %s: %v", turnServerAddr, connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
udpConn, dialErr := a.net.DialUDP("udp", nil, udpAddr)
|
||||
if dialErr != nil {
|
||||
a.log.Warnf("Failed to dial DTLS address %s: %v", turnServerAddr, dialErr)
|
||||
return
|
||||
}
|
||||
|
||||
conn, connectErr := dtls.ClientWithContext(ctx, udpConn, &dtls.Config{
|
||||
ServerName: url.Host,
|
||||
InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec
|
||||
})
|
||||
if connectErr != nil {
|
||||
a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr)
|
||||
return
|
||||
}
|
||||
|
||||
relAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert
|
||||
relPort = conn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert
|
||||
relayProtocol = relayProtocolDTLS
|
||||
locConn = &fakenet.PacketConn{Conn: conn}
|
||||
case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURNS:
|
||||
tcpAddr, resolvErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr)
|
||||
if resolvErr != nil {
|
||||
a.log.Warnf("Failed to resolve relay address %s: %v", turnServerAddr, resolvErr)
|
||||
return
|
||||
}
|
||||
|
||||
tcpConn, dialErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr)
|
||||
if dialErr != nil {
|
||||
a.log.Warnf("Failed to connect to relay: %v", dialErr)
|
||||
return
|
||||
}
|
||||
|
||||
conn := tls.Client(tcpConn, &tls.Config{
|
||||
ServerName: url.Host,
|
||||
InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec
|
||||
})
|
||||
|
||||
if hsErr := conn.HandshakeContext(ctx); hsErr != nil {
|
||||
if closeErr := tcpConn.Close(); closeErr != nil {
|
||||
a.log.Errorf("Failed to close relay connection: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to connect to relay: %v", hsErr)
|
||||
return
|
||||
}
|
||||
|
||||
relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert
|
||||
relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert
|
||||
relayProtocol = relayProtocolTLS
|
||||
locConn = turn.NewSTUNConn(conn)
|
||||
default:
|
||||
a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v", url)
|
||||
return
|
||||
}
|
||||
|
||||
client, err := turn.NewClient(&turn.ClientConfig{
|
||||
TURNServerAddr: turnServerAddr,
|
||||
Conn: locConn,
|
||||
Username: url.Username,
|
||||
Password: url.Password,
|
||||
LoggerFactory: a.loggerFactory,
|
||||
Net: a.net,
|
||||
})
|
||||
if err != nil {
|
||||
closeConnAndLog(locConn, a.log, "failed to create new TURN client %s %s", turnServerAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = client.Listen(); err != nil {
|
||||
client.Close()
|
||||
closeConnAndLog(locConn, a.log, "failed to listen on TURN client %s %s", turnServerAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
relayConn, err := client.Allocate()
|
||||
if err != nil {
|
||||
client.Close()
|
||||
closeConnAndLog(locConn, a.log, "failed to allocate on TURN client %s %s", turnServerAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
rAddr := relayConn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert
|
||||
relayConfig := CandidateRelayConfig{
|
||||
Network: network,
|
||||
Component: ComponentRTP,
|
||||
Address: rAddr.IP.String(),
|
||||
Port: rAddr.Port,
|
||||
RelAddr: relAddr,
|
||||
RelPort: relPort,
|
||||
RelayProtocol: relayProtocol,
|
||||
OnClose: func() error {
|
||||
client.Close()
|
||||
return locConn.Close()
|
||||
},
|
||||
}
|
||||
relayConnClose := func() {
|
||||
if relayConErr := relayConn.Close(); relayConErr != nil {
|
||||
a.log.Warnf("Failed to close relay %v", relayConErr)
|
||||
}
|
||||
}
|
||||
candidate, err := NewCandidateRelay(&relayConfig)
|
||||
if err != nil {
|
||||
relayConnClose()
|
||||
|
||||
client.Close()
|
||||
closeConnAndLog(locConn, a.log, "failed to create relay candidate: %s %s: %v", network, rAddr.String(), err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.addCandidate(ctx, candidate, relayConn); err != nil {
|
||||
relayConnClose()
|
||||
|
||||
if closeErr := candidate.close(); closeErr != nil {
|
||||
a.log.Warnf("Failed to close candidate: %v", closeErr)
|
||||
}
|
||||
a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err)
|
||||
}
|
||||
}(*urls[i])
|
||||
}
|
||||
}
|
||||
90
server/vendor/github.com/pion/ice/v2/ice.go
generated
vendored
Normal file
90
server/vendor/github.com/pion/ice/v2/ice.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
// ConnectionState is an enum showing the state of a ICE Connection
|
||||
type ConnectionState int
|
||||
|
||||
// List of supported States
|
||||
const (
|
||||
// ConnectionStateUnknown represents an unknown state
|
||||
ConnectionStateUnknown ConnectionState = iota
|
||||
|
||||
// ConnectionStateNew ICE agent is gathering addresses
|
||||
ConnectionStateNew
|
||||
|
||||
// ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match
|
||||
ConnectionStateChecking
|
||||
|
||||
// ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs
|
||||
ConnectionStateConnected
|
||||
|
||||
// ConnectionStateCompleted ICE agent has finished
|
||||
ConnectionStateCompleted
|
||||
|
||||
// ConnectionStateFailed ICE agent never could successfully connect
|
||||
ConnectionStateFailed
|
||||
|
||||
// ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state
|
||||
ConnectionStateDisconnected
|
||||
|
||||
// ConnectionStateClosed ICE agent has finished and is no longer handling requests
|
||||
ConnectionStateClosed
|
||||
)
|
||||
|
||||
func (c ConnectionState) String() string {
|
||||
switch c {
|
||||
case ConnectionStateNew:
|
||||
return "New"
|
||||
case ConnectionStateChecking:
|
||||
return "Checking"
|
||||
case ConnectionStateConnected:
|
||||
return "Connected"
|
||||
case ConnectionStateCompleted:
|
||||
return "Completed"
|
||||
case ConnectionStateFailed:
|
||||
return "Failed"
|
||||
case ConnectionStateDisconnected:
|
||||
return "Disconnected"
|
||||
case ConnectionStateClosed:
|
||||
return "Closed"
|
||||
default:
|
||||
return "Invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// GatheringState describes the state of the candidate gathering process
|
||||
type GatheringState int
|
||||
|
||||
const (
|
||||
// GatheringStateUnknown represents an unknown state
|
||||
GatheringStateUnknown GatheringState = iota
|
||||
|
||||
// GatheringStateNew indicates candidate gathering is not yet started
|
||||
GatheringStateNew
|
||||
|
||||
// GatheringStateGathering indicates candidate gathering is ongoing
|
||||
GatheringStateGathering
|
||||
|
||||
// GatheringStateComplete indicates candidate gathering has been completed
|
||||
GatheringStateComplete
|
||||
)
|
||||
|
||||
func (t GatheringState) String() string {
|
||||
switch t {
|
||||
case GatheringStateNew:
|
||||
return "new"
|
||||
case GatheringStateGathering:
|
||||
return "gathering"
|
||||
case GatheringStateComplete:
|
||||
return "complete"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
relayProtocolDTLS = "dtls"
|
||||
relayProtocolTLS = "tls"
|
||||
)
|
||||
90
server/vendor/github.com/pion/ice/v2/icecontrol.go
generated
vendored
Normal file
90
server/vendor/github.com/pion/ice/v2/icecontrol.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
// tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING}
|
||||
// and represents the so-called tiebreaker number.
|
||||
type tiebreaker uint64
|
||||
|
||||
const tiebreakerSize = 8 // 64 bit
|
||||
|
||||
// AddToAs adds tiebreaker value to m as t attribute.
|
||||
func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error {
|
||||
v := make([]byte, tiebreakerSize)
|
||||
binary.BigEndian.PutUint64(v, uint64(a))
|
||||
m.Add(t, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFromAs decodes tiebreaker value in message getting it as for t type.
|
||||
func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error {
|
||||
v, err := m.Get(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = stun.CheckSize(t, len(v), tiebreakerSize); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = tiebreaker(binary.BigEndian.Uint64(v))
|
||||
return nil
|
||||
}
|
||||
|
||||
// AttrControlled represents ICE-CONTROLLED attribute.
|
||||
type AttrControlled uint64
|
||||
|
||||
// AddTo adds ICE-CONTROLLED to message.
|
||||
func (c AttrControlled) AddTo(m *stun.Message) error {
|
||||
return tiebreaker(c).AddToAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// GetFrom decodes ICE-CONTROLLED from message.
|
||||
func (c *AttrControlled) GetFrom(m *stun.Message) error {
|
||||
return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// AttrControlling represents ICE-CONTROLLING attribute.
|
||||
type AttrControlling uint64
|
||||
|
||||
// AddTo adds ICE-CONTROLLING to message.
|
||||
func (c AttrControlling) AddTo(m *stun.Message) error {
|
||||
return tiebreaker(c).AddToAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
|
||||
// GetFrom decodes ICE-CONTROLLING from message.
|
||||
func (c *AttrControlling) GetFrom(m *stun.Message) error {
|
||||
return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
|
||||
// AttrControl is helper that wraps ICE-{CONTROLLED,CONTROLLING}.
|
||||
type AttrControl struct {
|
||||
Role Role
|
||||
Tiebreaker uint64
|
||||
}
|
||||
|
||||
// AddTo adds ICE-CONTROLLED or ICE-CONTROLLING attribute depending on Role.
|
||||
func (c AttrControl) AddTo(m *stun.Message) error {
|
||||
if c.Role == Controlling {
|
||||
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
|
||||
// GetFrom decodes Role and Tiebreaker value from message.
|
||||
func (c *AttrControl) GetFrom(m *stun.Message) error {
|
||||
if m.Contains(stun.AttrICEControlling) {
|
||||
c.Role = Controlling
|
||||
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling)
|
||||
}
|
||||
if m.Contains(stun.AttrICEControlled) {
|
||||
c.Role = Controlled
|
||||
return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled)
|
||||
}
|
||||
return stun.ErrAttributeNotFound
|
||||
}
|
||||
23
server/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go
generated
vendored
Normal file
23
server/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package atomic contains custom atomic types
|
||||
package atomic
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Error is an atomic error
|
||||
type Error struct {
|
||||
v atomic.Value
|
||||
}
|
||||
|
||||
// Store updates the value of the atomic variable
|
||||
func (a *Error) Store(err error) {
|
||||
a.v.Store(struct{ error }{err})
|
||||
}
|
||||
|
||||
// Load retrieves the current value of the atomic variable
|
||||
func (a *Error) Load() error {
|
||||
err, _ := a.v.Load().(struct{ error })
|
||||
return err.error
|
||||
}
|
||||
23
server/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go
generated
vendored
Normal file
23
server/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package fakenet
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MockPacketConn for tests
|
||||
type MockPacketConn struct{}
|
||||
|
||||
func (m *MockPacketConn) ReadFrom([]byte) (n int, addr net.Addr, err error) { return 0, nil, nil } //nolint:revive
|
||||
func (m *MockPacketConn) WriteTo([]byte, net.Addr) (n int, err error) { return 0, nil } //nolint:revive
|
||||
func (m *MockPacketConn) Close() error { return nil } //nolint:revive
|
||||
func (m *MockPacketConn) LocalAddr() net.Addr { return nil } //nolint:revive
|
||||
func (m *MockPacketConn) SetDeadline(time.Time) error { return nil } //nolint:revive
|
||||
func (m *MockPacketConn) SetReadDeadline(time.Time) error { return nil } //nolint:revive
|
||||
func (m *MockPacketConn) SetWriteDeadline(time.Time) error { return nil } //nolint:revive
|
||||
29
server/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go
generated
vendored
Normal file
29
server/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package fakenet contains fake network abstractions
|
||||
package fakenet
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Compile-time assertion
|
||||
var _ net.PacketConn = (*PacketConn)(nil)
|
||||
|
||||
// PacketConn wraps a net.Conn and emulates net.PacketConn
|
||||
type PacketConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
// ReadFrom reads a packet from the connection,
|
||||
func (f *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, err = f.Conn.Read(p)
|
||||
addr = f.Conn.RemoteAddr()
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo writes a packet with payload p to addr.
|
||||
func (f *PacketConn) WriteTo(p []byte, _ net.Addr) (int, error) {
|
||||
return f.Conn.Write(p)
|
||||
}
|
||||
72
server/vendor/github.com/pion/ice/v2/internal/stun/stun.go
generated
vendored
Normal file
72
server/vendor/github.com/pion/ice/v2/internal/stun/stun.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package stun contains ICE specific STUN code
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
var (
|
||||
errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response")
|
||||
errMismatchUsername = errors.New("username mismatch")
|
||||
)
|
||||
|
||||
// GetXORMappedAddr initiates a STUN requests to serverAddr using conn, reads the response and returns
|
||||
// the XORMappedAddress returned by the STUN server.
|
||||
func GetXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, timeout time.Duration) (*stun.XORMappedAddress, error) {
|
||||
if timeout > 0 {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reset timeout after completion
|
||||
defer conn.SetReadDeadline(time.Time{}) //nolint:errcheck
|
||||
}
|
||||
|
||||
req, err := stun.Build(stun.BindingRequest, stun.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = conn.WriteTo(req.Raw, serverAddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const maxMessageSize = 1280
|
||||
buf := make([]byte, maxMessageSize)
|
||||
n, _, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &stun.Message{Raw: buf[:n]}
|
||||
if err = res.Decode(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var addr stun.XORMappedAddress
|
||||
if err = addr.GetFrom(res); err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errGetXorMappedAddrResponse, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
return &addr, nil
|
||||
}
|
||||
|
||||
// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value
|
||||
func AssertUsername(m *stun.Message, expectedUsername string) error {
|
||||
var username stun.Username
|
||||
if err := username.GetFrom(m); err != nil {
|
||||
return err
|
||||
} else if string(username) != expectedUsername {
|
||||
return fmt.Errorf("%w expected(%x) actual(%x)", errMismatchUsername, expectedUsername, string(username))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
65
server/vendor/github.com/pion/ice/v2/mdns.go
generated
vendored
Normal file
65
server/vendor/github.com/pion/ice/v2/mdns.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/mdns"
|
||||
"github.com/pion/transport/v2"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// MulticastDNSMode represents the different Multicast modes ICE can run in
|
||||
type MulticastDNSMode byte
|
||||
|
||||
// MulticastDNSMode enum
|
||||
const (
|
||||
// MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs
|
||||
MulticastDNSModeDisabled MulticastDNSMode = iota + 1
|
||||
|
||||
// MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs
|
||||
MulticastDNSModeQueryOnly
|
||||
|
||||
// MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted, and local host candidates will use mDNS
|
||||
MulticastDNSModeQueryAndGather
|
||||
)
|
||||
|
||||
func generateMulticastDNSName() (string, error) {
|
||||
// https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html#gathering
|
||||
// The unique name MUST consist of a version 4 UUID as defined in [RFC4122], followed by “.local”.
|
||||
u, err := uuid.NewRandom()
|
||||
return u.String() + ".local", err
|
||||
}
|
||||
|
||||
func createMulticastDNS(n transport.Net, mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) {
|
||||
if mDNSMode == MulticastDNSModeDisabled {
|
||||
return nil, mDNSMode, nil
|
||||
}
|
||||
|
||||
addr, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddress)
|
||||
if mdnsErr != nil {
|
||||
return nil, mDNSMode, mdnsErr
|
||||
}
|
||||
|
||||
l, mdnsErr := n.ListenUDP("udp4", addr)
|
||||
if mdnsErr != nil {
|
||||
// If ICE fails to start MulticastDNS server just warn the user and continue
|
||||
log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr)
|
||||
return nil, MulticastDNSModeDisabled, nil
|
||||
}
|
||||
|
||||
switch mDNSMode {
|
||||
case MulticastDNSModeQueryOnly:
|
||||
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
|
||||
return conn, mDNSMode, err
|
||||
case MulticastDNSModeQueryAndGather:
|
||||
conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
|
||||
LocalNames: []string{mDNSName},
|
||||
})
|
||||
return conn, mDNSMode, err
|
||||
default:
|
||||
return nil, mDNSMode, nil
|
||||
}
|
||||
}
|
||||
137
server/vendor/github.com/pion/ice/v2/net.go
generated
vendored
Normal file
137
server/vendor/github.com/pion/ice/v2/net.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
// The conditions of invalidation written below are defined in
|
||||
// https://tools.ietf.org/html/rfc8445#section-5.1.1.1
|
||||
func isSupportedIPv6(ip net.IP) bool {
|
||||
if len(ip) != net.IPv6len ||
|
||||
isZeros(ip[0:12]) || // !(IPv4-compatible IPv6)
|
||||
ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast)
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
ip.IsLinkLocalMulticast() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isZeros(ip net.IP) bool {
|
||||
for i := 0; i < len(ip); i++ {
|
||||
if ip[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func localInterfaces(n transport.Net, interfaceFilter func(string) bool, ipFilter func(net.IP) bool, networkTypes []NetworkType, includeLoopback bool) ([]net.IP, error) { //nolint:gocognit
|
||||
ips := []net.IP{}
|
||||
ifaces, err := n.Interfaces()
|
||||
if err != nil {
|
||||
return ips, err
|
||||
}
|
||||
|
||||
var IPv4Requested, IPv6Requested bool
|
||||
for _, typ := range networkTypes {
|
||||
if typ.IsIPv4() {
|
||||
IPv4Requested = true
|
||||
}
|
||||
|
||||
if typ.IsIPv6() {
|
||||
IPv6Requested = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // Interface down
|
||||
}
|
||||
if (iface.Flags&net.FlagLoopback != 0) && !includeLoopback {
|
||||
continue // Loopback interface
|
||||
}
|
||||
|
||||
if interfaceFilter != nil && !interfaceFilter(iface.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch addr := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = addr.IP
|
||||
case *net.IPAddr:
|
||||
ip = addr.IP
|
||||
}
|
||||
if ip == nil || (ip.IsLoopback() && !includeLoopback) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipv4 := ip.To4(); ipv4 == nil {
|
||||
if !IPv6Requested {
|
||||
continue
|
||||
} else if !isSupportedIPv6(ip) {
|
||||
continue
|
||||
}
|
||||
} else if !IPv4Requested {
|
||||
continue
|
||||
}
|
||||
|
||||
if ipFilter != nil && !ipFilter(ip) {
|
||||
continue
|
||||
}
|
||||
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, portMin int, network string, lAddr *net.UDPAddr) (transport.UDPConn, error) {
|
||||
if (lAddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
|
||||
return n.ListenUDP(network, lAddr)
|
||||
}
|
||||
var i, j int
|
||||
i = portMin
|
||||
if i == 0 {
|
||||
i = 1
|
||||
}
|
||||
j = portMax
|
||||
if j == 0 {
|
||||
j = 0xFFFF
|
||||
}
|
||||
if i > j {
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
portStart := globalMathRandomGenerator.Intn(j-i+1) + i
|
||||
portCurrent := portStart
|
||||
for {
|
||||
lAddr = &net.UDPAddr{IP: lAddr.IP, Port: portCurrent}
|
||||
c, e := n.ListenUDP(network, lAddr)
|
||||
if e == nil {
|
||||
return c, e //nolint:nilerr
|
||||
}
|
||||
log.Debugf("Failed to listen %s: %v", lAddr.String(), e)
|
||||
portCurrent++
|
||||
if portCurrent > j {
|
||||
portCurrent = i
|
||||
}
|
||||
if portCurrent == portStart {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, ErrPort
|
||||
}
|
||||
137
server/vendor/github.com/pion/ice/v2/networktype.go
generated
vendored
Normal file
137
server/vendor/github.com/pion/ice/v2/networktype.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
udp = "udp"
|
||||
tcp = "tcp"
|
||||
udp4 = "udp4"
|
||||
udp6 = "udp6"
|
||||
tcp4 = "tcp4"
|
||||
tcp6 = "tcp6"
|
||||
)
|
||||
|
||||
func supportedNetworkTypes() []NetworkType {
|
||||
return []NetworkType{
|
||||
NetworkTypeUDP4,
|
||||
NetworkTypeUDP6,
|
||||
NetworkTypeTCP4,
|
||||
NetworkTypeTCP6,
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkType represents the type of network
|
||||
type NetworkType int
|
||||
|
||||
const (
|
||||
// NetworkTypeUDP4 indicates UDP over IPv4.
|
||||
NetworkTypeUDP4 NetworkType = iota + 1
|
||||
|
||||
// NetworkTypeUDP6 indicates UDP over IPv6.
|
||||
NetworkTypeUDP6
|
||||
|
||||
// NetworkTypeTCP4 indicates TCP over IPv4.
|
||||
NetworkTypeTCP4
|
||||
|
||||
// NetworkTypeTCP6 indicates TCP over IPv6.
|
||||
NetworkTypeTCP6
|
||||
)
|
||||
|
||||
func (t NetworkType) String() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4:
|
||||
return udp4
|
||||
case NetworkTypeUDP6:
|
||||
return udp6
|
||||
case NetworkTypeTCP4:
|
||||
return tcp4
|
||||
case NetworkTypeTCP6:
|
||||
return tcp6
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// IsUDP returns true when network is UDP4 or UDP6.
|
||||
func (t NetworkType) IsUDP() bool {
|
||||
return t == NetworkTypeUDP4 || t == NetworkTypeUDP6
|
||||
}
|
||||
|
||||
// IsTCP returns true when network is TCP4 or TCP6.
|
||||
func (t NetworkType) IsTCP() bool {
|
||||
return t == NetworkTypeTCP4 || t == NetworkTypeTCP6
|
||||
}
|
||||
|
||||
// NetworkShort returns the short network description
|
||||
func (t NetworkType) NetworkShort() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeUDP6:
|
||||
return udp
|
||||
case NetworkTypeTCP4, NetworkTypeTCP6:
|
||||
return tcp
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// IsReliable returns true if the network is reliable
|
||||
func (t NetworkType) IsReliable() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeUDP6:
|
||||
return false
|
||||
case NetworkTypeTCP4, NetworkTypeTCP6:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIPv4 returns whether the network type is IPv4 or not.
|
||||
func (t NetworkType) IsIPv4() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeTCP4:
|
||||
return true
|
||||
case NetworkTypeUDP6, NetworkTypeTCP6:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIPv6 returns whether the network type is IPv6 or not.
|
||||
func (t NetworkType) IsIPv6() bool {
|
||||
switch t {
|
||||
case NetworkTypeUDP4, NetworkTypeTCP4:
|
||||
return false
|
||||
case NetworkTypeUDP6, NetworkTypeTCP6:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// determineNetworkType determines the type of network based on
|
||||
// the short network string and an IP address.
|
||||
func determineNetworkType(network string, ip net.IP) (NetworkType, error) {
|
||||
ipv4 := ip.To4() != nil
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(strings.ToLower(network), udp):
|
||||
if ipv4 {
|
||||
return NetworkTypeUDP4, nil
|
||||
}
|
||||
return NetworkTypeUDP6, nil
|
||||
|
||||
case strings.HasPrefix(strings.ToLower(network), tcp):
|
||||
if ipv4 {
|
||||
return NetworkTypeTCP4, nil
|
||||
}
|
||||
return NetworkTypeTCP6, nil
|
||||
}
|
||||
|
||||
return NetworkType(0), fmt.Errorf("%w from %s %s", ErrDetermineNetworkType, network, ip)
|
||||
}
|
||||
36
server/vendor/github.com/pion/ice/v2/priority.go
generated
vendored
Normal file
36
server/vendor/github.com/pion/ice/v2/priority.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
// PriorityAttr represents PRIORITY attribute.
|
||||
type PriorityAttr uint32
|
||||
|
||||
const prioritySize = 4 // 32 bit
|
||||
|
||||
// AddTo adds PRIORITY attribute to message.
|
||||
func (p PriorityAttr) AddTo(m *stun.Message) error {
|
||||
v := make([]byte, prioritySize)
|
||||
binary.BigEndian.PutUint32(v, uint32(p))
|
||||
m.Add(stun.AttrPriority, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFrom decodes PRIORITY attribute from message.
|
||||
func (p *PriorityAttr) GetFrom(m *stun.Message) error {
|
||||
v, err := m.Get(stun.AttrPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = stun.CheckSize(stun.AttrPriority, len(v), prioritySize); err != nil {
|
||||
return err
|
||||
}
|
||||
*p = PriorityAttr(binary.BigEndian.Uint32(v))
|
||||
return nil
|
||||
}
|
||||
56
server/vendor/github.com/pion/ice/v2/rand.go
generated
vendored
Normal file
56
server/vendor/github.com/pion/ice/v2/rand.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "github.com/pion/randutil"
|
||||
|
||||
const (
|
||||
runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
runesDigit = "0123456789"
|
||||
runesCandidateIDFoundation = runesAlpha + runesDigit + "+/"
|
||||
|
||||
lenUFrag = 16
|
||||
lenPwd = 32
|
||||
)
|
||||
|
||||
// Seeding random generator each time limits number of generated sequence to 31-bits,
|
||||
// and causes collision on low time accuracy environments.
|
||||
// Use global random generator seeded by crypto grade random.
|
||||
var (
|
||||
globalMathRandomGenerator = randutil.NewMathRandomGenerator() //nolint:gochecknoglobals
|
||||
globalCandidateIDGenerator = candidateIDGenerator{globalMathRandomGenerator} //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
// candidateIDGenerator is a random candidate ID generator.
|
||||
// Candidate ID is used in SDP and always shared to the other peer.
|
||||
// It doesn't require cryptographic random.
|
||||
type candidateIDGenerator struct {
|
||||
randutil.MathRandomGenerator
|
||||
}
|
||||
|
||||
func newCandidateIDGenerator() *candidateIDGenerator {
|
||||
return &candidateIDGenerator{
|
||||
randutil.NewMathRandomGenerator(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *candidateIDGenerator) Generate() string {
|
||||
// https://tools.ietf.org/html/rfc5245#section-15.1
|
||||
// candidate-id = "candidate" ":" foundation
|
||||
// foundation = 1*32ice-char
|
||||
// ice-char = ALPHA / DIGIT / "+" / "/"
|
||||
return "candidate:" + g.MathRandomGenerator.GenerateString(32, runesCandidateIDFoundation)
|
||||
}
|
||||
|
||||
// generatePwd generates ICE pwd.
|
||||
// This internally uses generateCryptoRandomString.
|
||||
func generatePwd() (string, error) {
|
||||
return randutil.GenerateCryptoRandomString(lenPwd, runesAlpha)
|
||||
}
|
||||
|
||||
// generateUFrag generates ICE user fragment.
|
||||
// This internally uses generateCryptoRandomString.
|
||||
func generateUFrag() (string, error) {
|
||||
return randutil.GenerateCryptoRandomString(lenUFrag, runesAlpha)
|
||||
}
|
||||
6
server/vendor/github.com/pion/ice/v2/renovate.json
generated
vendored
Normal file
6
server/vendor/github.com/pion/ice/v2/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>pion/renovate-config"
|
||||
]
|
||||
}
|
||||
46
server/vendor/github.com/pion/ice/v2/role.go
generated
vendored
Normal file
46
server/vendor/github.com/pion/ice/v2/role.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Role represents ICE agent role, which can be controlling or controlled.
|
||||
type Role byte
|
||||
|
||||
// Possible ICE agent roles.
|
||||
const (
|
||||
Controlling Role = iota
|
||||
Controlled
|
||||
)
|
||||
|
||||
// UnmarshalText implements TextUnmarshaler.
|
||||
func (r *Role) UnmarshalText(text []byte) error {
|
||||
switch string(text) {
|
||||
case "controlling":
|
||||
*r = Controlling
|
||||
case "controlled":
|
||||
*r = Controlled
|
||||
default:
|
||||
return fmt.Errorf("%w %q", errUnknownRole, text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText implements TextMarshaler.
|
||||
func (r Role) MarshalText() (text []byte, err error) {
|
||||
return []byte(r.String()), nil
|
||||
}
|
||||
|
||||
func (r Role) String() string {
|
||||
switch r {
|
||||
case Controlling:
|
||||
return "controlling"
|
||||
case Controlled:
|
||||
return "controlled"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
312
server/vendor/github.com/pion/ice/v2/selection.go
generated
vendored
Normal file
312
server/vendor/github.com/pion/ice/v2/selection.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
type pairCandidateSelector interface {
|
||||
Start()
|
||||
ContactCandidates()
|
||||
PingCandidate(local, remote Candidate)
|
||||
HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr)
|
||||
HandleBindingRequest(m *stun.Message, local, remote Candidate)
|
||||
}
|
||||
|
||||
type controllingSelector struct {
|
||||
startTime time.Time
|
||||
agent *Agent
|
||||
nominatedPair *CandidatePair
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
func (s *controllingSelector) Start() {
|
||||
s.startTime = time.Now()
|
||||
s.nominatedPair = nil
|
||||
}
|
||||
|
||||
func (s *controllingSelector) isNominatable(c Candidate) bool {
|
||||
switch {
|
||||
case c.Type() == CandidateTypeHost:
|
||||
return time.Since(s.startTime).Nanoseconds() > s.agent.hostAcceptanceMinWait.Nanoseconds()
|
||||
case c.Type() == CandidateTypeServerReflexive:
|
||||
return time.Since(s.startTime).Nanoseconds() > s.agent.srflxAcceptanceMinWait.Nanoseconds()
|
||||
case c.Type() == CandidateTypePeerReflexive:
|
||||
return time.Since(s.startTime).Nanoseconds() > s.agent.prflxAcceptanceMinWait.Nanoseconds()
|
||||
case c.Type() == CandidateTypeRelay:
|
||||
return time.Since(s.startTime).Nanoseconds() > s.agent.relayAcceptanceMinWait.Nanoseconds()
|
||||
}
|
||||
|
||||
s.log.Errorf("Invalid candidate type: %s", c.Type())
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *controllingSelector) ContactCandidates() {
|
||||
switch {
|
||||
case s.agent.getSelectedPair() != nil:
|
||||
if s.agent.validateSelectedPair() {
|
||||
s.log.Trace("Checking keepalive")
|
||||
s.agent.checkKeepalive()
|
||||
}
|
||||
case s.nominatedPair != nil:
|
||||
s.nominatePair(s.nominatedPair)
|
||||
default:
|
||||
p := s.agent.getBestValidCandidatePair()
|
||||
if p != nil && s.isNominatable(p.Local) && s.isNominatable(p.Remote) {
|
||||
s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.Local, p.Remote)
|
||||
p.nominated = true
|
||||
s.nominatedPair = p
|
||||
s.nominatePair(p)
|
||||
return
|
||||
}
|
||||
s.agent.pingAllCandidates()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *controllingSelector) nominatePair(pair *CandidatePair) {
|
||||
// The controlling agent MUST include the USE-CANDIDATE attribute in
|
||||
// order to nominate a candidate pair (Section 8.1.1). The controlled
|
||||
// agent MUST NOT include the USE-CANDIDATE attribute in a Binding
|
||||
// request.
|
||||
msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
|
||||
stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
|
||||
UseCandidate(),
|
||||
AttrControlling(s.agent.tieBreaker),
|
||||
PriorityAttr(pair.Local.Priority()),
|
||||
stun.NewShortTermIntegrity(s.agent.remotePwd),
|
||||
stun.Fingerprint,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Tracef("Ping STUN (nominate candidate pair) from %s to %s", pair.Local, pair.Remote)
|
||||
s.agent.sendBindingRequest(msg, pair.Local, pair.Remote)
|
||||
}
|
||||
|
||||
func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
|
||||
s.agent.sendBindingSuccess(m, local, remote)
|
||||
|
||||
p := s.agent.findPair(local, remote)
|
||||
|
||||
if p == nil {
|
||||
s.agent.addPair(local, remote)
|
||||
return
|
||||
}
|
||||
|
||||
if p.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil {
|
||||
bestPair := s.agent.getBestAvailableCandidatePair()
|
||||
if bestPair == nil {
|
||||
s.log.Tracef("No best pair available")
|
||||
} else if bestPair.equal(p) && s.isNominatable(p.Local) && s.isNominatable(p.Remote) {
|
||||
s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated", p.Local, p.Remote)
|
||||
s.nominatedPair = p
|
||||
s.nominatePair(p)
|
||||
}
|
||||
}
|
||||
|
||||
if s.agent.userBindingRequestHandler != nil {
|
||||
if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch {
|
||||
s.agent.setSelectedPair(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) {
|
||||
ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID)
|
||||
if !ok {
|
||||
s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
|
||||
return
|
||||
}
|
||||
|
||||
transactionAddr := pendingRequest.destination
|
||||
|
||||
// Assert that NAT is not symmetric
|
||||
// https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
|
||||
if !addrEqual(transactionAddr, remoteAddr) {
|
||||
s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local)
|
||||
p := s.agent.findPair(local, remote)
|
||||
|
||||
if p == nil {
|
||||
// This shouldn't happen
|
||||
s.log.Error("Success response from invalid candidate pair")
|
||||
return
|
||||
}
|
||||
|
||||
p.state = CandidatePairStateSucceeded
|
||||
s.log.Tracef("Found valid candidate pair: %s", p)
|
||||
if pendingRequest.isUseCandidate && s.agent.getSelectedPair() == nil {
|
||||
s.agent.setSelectedPair(p)
|
||||
}
|
||||
|
||||
p.UpdateRoundTripTime(rtt)
|
||||
}
|
||||
|
||||
func (s *controllingSelector) PingCandidate(local, remote Candidate) {
|
||||
msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
|
||||
stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
|
||||
AttrControlling(s.agent.tieBreaker),
|
||||
PriorityAttr(local.Priority()),
|
||||
stun.NewShortTermIntegrity(s.agent.remotePwd),
|
||||
stun.Fingerprint,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.agent.sendBindingRequest(msg, local, remote)
|
||||
}
|
||||
|
||||
type controlledSelector struct {
|
||||
agent *Agent
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
func (s *controlledSelector) Start() {
|
||||
}
|
||||
|
||||
func (s *controlledSelector) ContactCandidates() {
|
||||
if s.agent.getSelectedPair() != nil {
|
||||
if s.agent.validateSelectedPair() {
|
||||
s.log.Trace("Checking keepalive")
|
||||
s.agent.checkKeepalive()
|
||||
}
|
||||
} else {
|
||||
s.agent.pingAllCandidates()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *controlledSelector) PingCandidate(local, remote Candidate) {
|
||||
msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
|
||||
stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
|
||||
AttrControlled(s.agent.tieBreaker),
|
||||
PriorityAttr(local.Priority()),
|
||||
stun.NewShortTermIntegrity(s.agent.remotePwd),
|
||||
stun.Fingerprint,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.agent.sendBindingRequest(msg, local, remote)
|
||||
}
|
||||
|
||||
func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) {
|
||||
//nolint:godox
|
||||
// TODO according to the standard we should specifically answer a failed nomination:
|
||||
// https://tools.ietf.org/html/rfc8445#section-7.3.1.5
|
||||
// If the controlled agent does not accept the request from the
|
||||
// controlling agent, the controlled agent MUST reject the nomination
|
||||
// request with an appropriate error code response (e.g., 400)
|
||||
// [RFC5389].
|
||||
|
||||
ok, pendingRequest, rtt := s.agent.handleInboundBindingSuccess(m.TransactionID)
|
||||
if !ok {
|
||||
s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
|
||||
return
|
||||
}
|
||||
|
||||
transactionAddr := pendingRequest.destination
|
||||
|
||||
// Assert that NAT is not symmetric
|
||||
// https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
|
||||
if !addrEqual(transactionAddr, remoteAddr) {
|
||||
s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
|
||||
return
|
||||
}
|
||||
|
||||
s.log.Tracef("Inbound STUN (SuccessResponse) from %s to %s", remote, local)
|
||||
|
||||
p := s.agent.findPair(local, remote)
|
||||
if p == nil {
|
||||
// This shouldn't happen
|
||||
s.log.Error("Success response from invalid candidate pair")
|
||||
return
|
||||
}
|
||||
|
||||
p.state = CandidatePairStateSucceeded
|
||||
s.log.Tracef("Found valid candidate pair: %s", p)
|
||||
if p.nominateOnBindingSuccess {
|
||||
if selectedPair := s.agent.getSelectedPair(); selectedPair == nil ||
|
||||
(selectedPair != p && selectedPair.priority() <= p.priority()) {
|
||||
s.agent.setSelectedPair(p)
|
||||
} else if selectedPair != p {
|
||||
s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair)
|
||||
}
|
||||
}
|
||||
|
||||
p.UpdateRoundTripTime(rtt)
|
||||
}
|
||||
|
||||
func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
|
||||
p := s.agent.findPair(local, remote)
|
||||
if p == nil {
|
||||
p = s.agent.addPair(local, remote)
|
||||
}
|
||||
|
||||
if m.Contains(stun.AttrUseCandidate) {
|
||||
// https://tools.ietf.org/html/rfc8445#section-7.3.1.5
|
||||
|
||||
if p.state == CandidatePairStateSucceeded {
|
||||
// If the state of this pair is Succeeded, it means that the check
|
||||
// previously sent by this pair produced a successful response and
|
||||
// generated a valid pair (Section 7.2.5.3.2). The agent sets the
|
||||
// nominated flag value of the valid pair to true.
|
||||
selectedPair := s.agent.getSelectedPair()
|
||||
if selectedPair == nil || (selectedPair != p && selectedPair.priority() <= p.priority()) {
|
||||
s.agent.setSelectedPair(p)
|
||||
} else if selectedPair != p {
|
||||
s.log.Tracef("Ignore nominate new pair %s, already nominated pair %s", p, selectedPair)
|
||||
}
|
||||
} else {
|
||||
// If the received Binding request triggered a new check to be
|
||||
// enqueued in the triggered-check queue (Section 7.3.1.4), once the
|
||||
// check is sent and if it generates a successful response, and
|
||||
// generates a valid pair, the agent sets the nominated flag of the
|
||||
// pair to true. If the request fails (Section 7.2.5.2), the agent
|
||||
// MUST remove the candidate pair from the valid list, set the
|
||||
// candidate pair state to Failed, and set the checklist state to
|
||||
// Failed.
|
||||
p.nominateOnBindingSuccess = true
|
||||
}
|
||||
}
|
||||
|
||||
s.agent.sendBindingSuccess(m, local, remote)
|
||||
s.PingCandidate(local, remote)
|
||||
|
||||
if s.agent.userBindingRequestHandler != nil {
|
||||
if shouldSwitch := s.agent.userBindingRequestHandler(m, local, remote, p); shouldSwitch {
|
||||
s.agent.setSelectedPair(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type liteSelector struct {
|
||||
pairCandidateSelector
|
||||
}
|
||||
|
||||
// A lite selector should not contact candidates
|
||||
func (s *liteSelector) ContactCandidates() {
|
||||
if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
|
||||
//nolint:godox
|
||||
// https://github.com/pion/ice/issues/96
|
||||
// TODO: implement lite controlling agent. For now falling back to full agent.
|
||||
// This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2
|
||||
s.pairCandidateSelector.ContactCandidates()
|
||||
} else if v, ok := s.pairCandidateSelector.(*controlledSelector); ok {
|
||||
v.agent.validateSelectedPair()
|
||||
}
|
||||
}
|
||||
180
server/vendor/github.com/pion/ice/v2/stats.go
generated
vendored
Normal file
180
server/vendor/github.com/pion/ice/v2/stats.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// CandidatePairStats contains ICE candidate pair statistics
|
||||
type CandidatePairStats struct {
|
||||
// Timestamp is the timestamp associated with this object.
|
||||
Timestamp time.Time
|
||||
|
||||
// LocalCandidateID is the ID of the local candidate
|
||||
LocalCandidateID string
|
||||
|
||||
// RemoteCandidateID is the ID of the remote candidate
|
||||
RemoteCandidateID string
|
||||
|
||||
// State represents the state of the checklist for the local and remote
|
||||
// candidates in a pair.
|
||||
State CandidatePairState
|
||||
|
||||
// Nominated is true when this valid pair that should be used for media
|
||||
// if it is the highest-priority one amongst those whose nominated flag is set
|
||||
Nominated bool
|
||||
|
||||
// PacketsSent represents the total number of packets sent on this candidate pair.
|
||||
PacketsSent uint32
|
||||
|
||||
// PacketsReceived represents the total number of packets received on this candidate pair.
|
||||
PacketsReceived uint32
|
||||
|
||||
// BytesSent represents the total number of payload bytes sent on this candidate pair
|
||||
// not including headers or padding.
|
||||
BytesSent uint64
|
||||
|
||||
// BytesReceived represents the total number of payload bytes received on this candidate pair
|
||||
// not including headers or padding.
|
||||
BytesReceived uint64
|
||||
|
||||
// LastPacketSentTimestamp represents the timestamp at which the last packet was
|
||||
// sent on this particular candidate pair, excluding STUN packets.
|
||||
LastPacketSentTimestamp time.Time
|
||||
|
||||
// LastPacketReceivedTimestamp represents the timestamp at which the last packet
|
||||
// was received on this particular candidate pair, excluding STUN packets.
|
||||
LastPacketReceivedTimestamp time.Time
|
||||
|
||||
// FirstRequestTimestamp represents the timestamp at which the first STUN request
|
||||
// was sent on this particular candidate pair.
|
||||
FirstRequestTimestamp time.Time
|
||||
|
||||
// LastRequestTimestamp represents the timestamp at which the last STUN request
|
||||
// was sent on this particular candidate pair. The average interval between two
|
||||
// consecutive connectivity checks sent can be calculated with
|
||||
// (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent.
|
||||
LastRequestTimestamp time.Time
|
||||
|
||||
// LastResponseTimestamp represents the timestamp at which the last STUN response
|
||||
// was received on this particular candidate pair.
|
||||
LastResponseTimestamp time.Time
|
||||
|
||||
// TotalRoundTripTime represents the sum of all round trip time measurements
|
||||
// in seconds since the beginning of the session, based on STUN connectivity
|
||||
// check responses (ResponsesReceived), including those that reply to requests
|
||||
// that are sent in order to verify consent. The average round trip time can
|
||||
// be computed from TotalRoundTripTime by dividing it by ResponsesReceived.
|
||||
TotalRoundTripTime float64
|
||||
|
||||
// CurrentRoundTripTime represents the latest round trip time measured in seconds,
|
||||
// computed from both STUN connectivity checks, including those that are sent
|
||||
// for consent verification.
|
||||
CurrentRoundTripTime float64
|
||||
|
||||
// AvailableOutgoingBitrate is calculated by the underlying congestion control
|
||||
// by combining the available bitrate for all the outgoing RTP streams using
|
||||
// this candidate pair. The bitrate measurement does not count the size of the
|
||||
// IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
|
||||
// in RFC 3890, i.e., it is measured in bits per second and the bitrate is calculated
|
||||
// over a 1 second window.
|
||||
AvailableOutgoingBitrate float64
|
||||
|
||||
// AvailableIncomingBitrate is calculated by the underlying congestion control
|
||||
// by combining the available bitrate for all the incoming RTP streams using
|
||||
// this candidate pair. The bitrate measurement does not count the size of the
|
||||
// IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
|
||||
// in RFC 3890, i.e., it is measured in bits per second and the bitrate is
|
||||
// calculated over a 1 second window.
|
||||
AvailableIncomingBitrate float64
|
||||
|
||||
// CircuitBreakerTriggerCount represents the number of times the circuit breaker
|
||||
// is triggered for this particular 5-tuple, ceasing transmission.
|
||||
CircuitBreakerTriggerCount uint32
|
||||
|
||||
// RequestsReceived represents the total number of connectivity check requests
|
||||
// received (including retransmissions). It is impossible for the receiver to
|
||||
// tell whether the request was sent in order to check connectivity or check
|
||||
// consent, so all connectivity checks requests are counted here.
|
||||
RequestsReceived uint64
|
||||
|
||||
// RequestsSent represents the total number of connectivity check requests
|
||||
// sent (not including retransmissions).
|
||||
RequestsSent uint64
|
||||
|
||||
// ResponsesReceived represents the total number of connectivity check responses received.
|
||||
ResponsesReceived uint64
|
||||
|
||||
// ResponsesSent represents the total number of connectivity check responses sent.
|
||||
// Since we cannot distinguish connectivity check requests and consent requests,
|
||||
// all responses are counted.
|
||||
ResponsesSent uint64
|
||||
|
||||
// RetransmissionsReceived represents the total number of connectivity check
|
||||
// request retransmissions received.
|
||||
RetransmissionsReceived uint64
|
||||
|
||||
// RetransmissionsSent represents the total number of connectivity check
|
||||
// request retransmissions sent.
|
||||
RetransmissionsSent uint64
|
||||
|
||||
// ConsentRequestsSent represents the total number of consent requests sent.
|
||||
ConsentRequestsSent uint64
|
||||
|
||||
// ConsentExpiredTimestamp represents the timestamp at which the latest valid
|
||||
// STUN binding response expired.
|
||||
ConsentExpiredTimestamp time.Time
|
||||
}
|
||||
|
||||
// CandidateStats contains ICE candidate statistics related to the ICETransport objects.
|
||||
type CandidateStats struct {
|
||||
// Timestamp is the timestamp associated with this object.
|
||||
Timestamp time.Time
|
||||
|
||||
// ID is the candidate ID
|
||||
ID string
|
||||
|
||||
// NetworkType represents the type of network interface used by the base of a
|
||||
// local candidate (the address the ICE agent sends from). Only present for
|
||||
// local candidates; it's not possible to know what type of network interface
|
||||
// a remote candidate is using.
|
||||
//
|
||||
// Note:
|
||||
// This stat only tells you about the network interface used by the first "hop";
|
||||
// it's possible that a connection will be bottlenecked by another type of network.
|
||||
// For example, when using Wi-Fi tethering, the networkType of the relevant candidate
|
||||
// would be "wifi", even when the next hop is over a cellular connection.
|
||||
NetworkType NetworkType
|
||||
|
||||
// IP is the IP address of the candidate, allowing for IPv4 addresses and
|
||||
// IPv6 addresses, but fully qualified domain names (FQDNs) are not allowed.
|
||||
IP string
|
||||
|
||||
// Port is the port number of the candidate.
|
||||
Port int
|
||||
|
||||
// CandidateType is the "Type" field of the ICECandidate.
|
||||
CandidateType CandidateType
|
||||
|
||||
// Priority is the "Priority" field of the ICECandidate.
|
||||
Priority uint32
|
||||
|
||||
// URL is the URL of the TURN or STUN server indicated in the that translated
|
||||
// this IP address. It is the URL address surfaced in an PeerConnectionICEEvent.
|
||||
URL string
|
||||
|
||||
// RelayProtocol is the protocol used by the endpoint to communicate with the
|
||||
// TURN server. This is only present for local candidates. Valid values for
|
||||
// the TURN URL protocol is one of UDP, TCP, or TLS.
|
||||
RelayProtocol string
|
||||
|
||||
// Deleted is true if the candidate has been deleted/freed. For host candidates,
|
||||
// this means that any network resources (typically a socket) associated with the
|
||||
// candidate have been released. For TURN candidates, this means the TURN allocation
|
||||
// is no longer active.
|
||||
//
|
||||
// Only defined for local candidates. For remote candidates, this property is not applicable.
|
||||
Deleted bool
|
||||
}
|
||||
435
server/vendor/github.com/pion/ice/v2/tcp_mux.go
generated
vendored
Normal file
435
server/vendor/github.com/pion/ice/v2/tcp_mux.go
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
// ErrGetTransportAddress can't convert net.Addr to underlying type (UDPAddr or TCPAddr).
|
||||
var ErrGetTransportAddress = errors.New("failed to get local transport address")
|
||||
|
||||
// TCPMux is allows grouping multiple TCP net.Conns and using them like UDP
|
||||
// net.PacketConns. The main implementation of this is TCPMuxDefault, and this
|
||||
// interface exists to allow mocking in tests.
|
||||
type TCPMux interface {
|
||||
io.Closer
|
||||
GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error)
|
||||
RemoveConnByUfrag(ufrag string)
|
||||
}
|
||||
|
||||
type ipAddr string
|
||||
|
||||
// TCPMuxDefault muxes TCP net.Conns into net.PacketConns and groups them by
|
||||
// Ufrag. It is a default implementation of TCPMux interface.
|
||||
type TCPMuxDefault struct {
|
||||
params *TCPMuxParams
|
||||
closed bool
|
||||
|
||||
// connsIPv4 and connsIPv6 are maps of all tcpPacketConns indexed by ufrag and local address
|
||||
connsIPv4, connsIPv6 map[string]map[ipAddr]*tcpPacketConn
|
||||
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// TCPMuxParams are parameters for TCPMux.
|
||||
type TCPMuxParams struct {
|
||||
Listener net.Listener
|
||||
Logger logging.LeveledLogger
|
||||
ReadBufferSize int
|
||||
|
||||
// Maximum buffer size for write op. 0 means no write buffer, the write op will block until the whole packet is written
|
||||
// if the write buffer is full, the subsequent write packet will be dropped until it has enough space.
|
||||
// a default 4MB is recommended.
|
||||
WriteBufferSize int
|
||||
|
||||
// A new established connection will be removed if the first STUN binding request is not received within this timeout,
|
||||
// avoiding the client with bad network or attacker to create a lot of empty connections.
|
||||
// Default 30s timeout will be used if not set.
|
||||
FirstStunBindTimeout time.Duration
|
||||
|
||||
// TCPMux will create connection from STUN binding request with an unknown username, if
|
||||
// the connection is not used in the timeout, it will be removed to avoid resource leak / attack.
|
||||
// Default 30s timeout will be used if not set.
|
||||
AliveDurationForConnFromStun time.Duration
|
||||
}
|
||||
|
||||
// NewTCPMuxDefault creates a new instance of TCPMuxDefault.
|
||||
func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault {
|
||||
if params.Logger == nil {
|
||||
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
|
||||
}
|
||||
|
||||
if params.FirstStunBindTimeout == 0 {
|
||||
params.FirstStunBindTimeout = 30 * time.Second
|
||||
}
|
||||
|
||||
if params.AliveDurationForConnFromStun == 0 {
|
||||
params.AliveDurationForConnFromStun = 30 * time.Second
|
||||
}
|
||||
|
||||
m := &TCPMuxDefault{
|
||||
params: ¶ms,
|
||||
|
||||
connsIPv4: map[string]map[ipAddr]*tcpPacketConn{},
|
||||
connsIPv6: map[string]map[ipAddr]*tcpPacketConn{},
|
||||
}
|
||||
|
||||
m.wg.Add(1)
|
||||
go func() {
|
||||
defer m.wg.Done()
|
||||
m.start()
|
||||
}()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) start() {
|
||||
m.params.Logger.Infof("Listening TCP on %s", m.params.Listener.Addr())
|
||||
for {
|
||||
conn, err := m.params.Listener.Accept()
|
||||
if err != nil {
|
||||
m.params.Logger.Infof("Error accepting connection: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
m.params.Logger.Debugf("Accepted connection from: %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
m.wg.Add(1)
|
||||
go func() {
|
||||
defer m.wg.Done()
|
||||
m.handleConn(conn)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// LocalAddr returns the listening address of this TCPMuxDefault.
|
||||
func (m *TCPMuxDefault) LocalAddr() net.Addr {
|
||||
return m.params.Listener.Addr()
|
||||
}
|
||||
|
||||
// GetConnByUfrag retrieves an existing or creates a new net.PacketConn.
|
||||
func (m *TCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.closed {
|
||||
return nil, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
if conn, ok := m.getConn(ufrag, isIPv6, local); ok {
|
||||
conn.ClearAliveTimer()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
return m.createConn(ufrag, isIPv6, local, false)
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP, fromStun bool) (*tcpPacketConn, error) {
|
||||
addr, ok := m.LocalAddr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
return nil, ErrGetTransportAddress
|
||||
}
|
||||
localAddr := *addr
|
||||
localAddr.IP = local
|
||||
|
||||
var alive time.Duration
|
||||
if fromStun {
|
||||
alive = m.params.AliveDurationForConnFromStun
|
||||
}
|
||||
|
||||
conn := newTCPPacketConn(tcpPacketParams{
|
||||
ReadBuffer: m.params.ReadBufferSize,
|
||||
WriteBuffer: m.params.WriteBufferSize,
|
||||
LocalAddr: &localAddr,
|
||||
Logger: m.params.Logger,
|
||||
AliveDuration: alive,
|
||||
})
|
||||
|
||||
var conns map[ipAddr]*tcpPacketConn
|
||||
if isIPv6 {
|
||||
if conns, ok = m.connsIPv6[ufrag]; !ok {
|
||||
conns = make(map[ipAddr]*tcpPacketConn)
|
||||
m.connsIPv6[ufrag] = conns
|
||||
}
|
||||
} else {
|
||||
if conns, ok = m.connsIPv4[ufrag]; !ok {
|
||||
conns = make(map[ipAddr]*tcpPacketConn)
|
||||
m.connsIPv4[ufrag] = conns
|
||||
}
|
||||
}
|
||||
conns[ipAddr(local.String())] = conn
|
||||
|
||||
m.wg.Add(1)
|
||||
go func() {
|
||||
defer m.wg.Done()
|
||||
<-conn.CloseChannel()
|
||||
m.removeConnByUfragAndLocalHost(ufrag, local)
|
||||
}()
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) closeAndLogError(closer io.Closer) {
|
||||
err := closer.Close()
|
||||
if err != nil {
|
||||
m.params.Logger.Warnf("Error closing connection: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) handleConn(conn net.Conn) {
|
||||
buf := make([]byte, 512)
|
||||
|
||||
if m.params.FirstStunBindTimeout > 0 {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(m.params.FirstStunBindTimeout)); err != nil {
|
||||
m.params.Logger.Warnf("Failed to set read deadline for first STUN message: %s to %s , err: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
}
|
||||
}
|
||||
n, err := readStreamingPacket(conn, buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.ErrShortBuffer) {
|
||||
m.params.Logger.Warnf("Buffer too small for first packet from %s: %s", conn.RemoteAddr(), err)
|
||||
} else {
|
||||
m.params.Logger.Warnf("Error reading first packet from %s: %s", conn.RemoteAddr(), err)
|
||||
}
|
||||
m.closeAndLogError(conn)
|
||||
return
|
||||
}
|
||||
if err = conn.SetReadDeadline(time.Time{}); err != nil {
|
||||
m.params.Logger.Warnf("Failed to reset read deadline from %s: %s", conn.RemoteAddr(), err)
|
||||
}
|
||||
|
||||
buf = buf[:n]
|
||||
|
||||
msg := &stun.Message{
|
||||
Raw: make([]byte, len(buf)),
|
||||
}
|
||||
// Explicitly copy raw buffer so Message can own the memory.
|
||||
copy(msg.Raw, buf)
|
||||
if err = msg.Decode(); err != nil {
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
|
||||
if m == nil || msg.Type.Method != stun.MethodBinding { // Not a STUN
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Not a STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
|
||||
for _, attr := range msg.Attributes {
|
||||
m.params.Logger.Debugf("Message attribute: %s", attr.String())
|
||||
}
|
||||
|
||||
attr, err := msg.Get(stun.AttrUsername)
|
||||
if err != nil {
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
|
||||
ufrag := strings.Split(string(attr), ":")[0]
|
||||
m.params.Logger.Debugf("Ufrag: %s", ufrag)
|
||||
|
||||
host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
||||
if err != nil {
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Failed to get host in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
|
||||
isIPv6 := net.ParseIP(host).To4() == nil
|
||||
|
||||
localAddr, ok := conn.LocalAddr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Failed to get local tcp address in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
packetConn, ok := m.getConn(ufrag, isIPv6, localAddr.IP)
|
||||
if !ok {
|
||||
packetConn, err = m.createConn(ufrag, isIPv6, localAddr.IP, true)
|
||||
if err != nil {
|
||||
m.mu.Unlock()
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Failed to create packetConn for STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||
return
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
if err := packetConn.AddConn(conn, buf); err != nil {
|
||||
m.closeAndLogError(conn)
|
||||
m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the listener and waits for all goroutines to exit.
|
||||
func (m *TCPMuxDefault) Close() error {
|
||||
m.mu.Lock()
|
||||
m.closed = true
|
||||
|
||||
for _, conns := range m.connsIPv4 {
|
||||
for _, conn := range conns {
|
||||
m.closeAndLogError(conn)
|
||||
}
|
||||
}
|
||||
for _, conns := range m.connsIPv6 {
|
||||
for _, conn := range conns {
|
||||
m.closeAndLogError(conn)
|
||||
}
|
||||
}
|
||||
|
||||
m.connsIPv4 = map[string]map[ipAddr]*tcpPacketConn{}
|
||||
m.connsIPv6 = map[string]map[ipAddr]*tcpPacketConn{}
|
||||
|
||||
err := m.params.Listener.Close()
|
||||
|
||||
m.mu.Unlock()
|
||||
|
||||
m.wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveConnByUfrag closes and removes a net.PacketConn by Ufrag.
|
||||
func (m *TCPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
||||
removedConns := make([]*tcpPacketConn, 0, 4)
|
||||
|
||||
// Keep lock section small to avoid deadlock with conn lock
|
||||
m.mu.Lock()
|
||||
if conns, ok := m.connsIPv4[ufrag]; ok {
|
||||
delete(m.connsIPv4, ufrag)
|
||||
for _, conn := range conns {
|
||||
removedConns = append(removedConns, conn)
|
||||
}
|
||||
}
|
||||
if conns, ok := m.connsIPv6[ufrag]; ok {
|
||||
delete(m.connsIPv6, ufrag)
|
||||
for _, conn := range conns {
|
||||
removedConns = append(removedConns, conn)
|
||||
}
|
||||
}
|
||||
|
||||
m.mu.Unlock()
|
||||
|
||||
// Close the connections outside the critical section to avoid
|
||||
// deadlocking TCP mux if (*tcpPacketConn).Close() blocks.
|
||||
for _, conn := range removedConns {
|
||||
m.closeAndLogError(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP) {
|
||||
removedConns := make([]*tcpPacketConn, 0, 4)
|
||||
|
||||
localIP := ipAddr(local.String())
|
||||
// Keep lock section small to avoid deadlock with conn lock
|
||||
m.mu.Lock()
|
||||
if conns, ok := m.connsIPv4[ufrag]; ok {
|
||||
if conn, ok := conns[localIP]; ok {
|
||||
delete(conns, localIP)
|
||||
if len(conns) == 0 {
|
||||
delete(m.connsIPv4, ufrag)
|
||||
}
|
||||
removedConns = append(removedConns, conn)
|
||||
}
|
||||
}
|
||||
if conns, ok := m.connsIPv6[ufrag]; ok {
|
||||
if conn, ok := conns[localIP]; ok {
|
||||
delete(conns, localIP)
|
||||
if len(conns) == 0 {
|
||||
delete(m.connsIPv6, ufrag)
|
||||
}
|
||||
removedConns = append(removedConns, conn)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
// Close the connections outside the critical section to avoid
|
||||
// deadlocking TCP mux if (*tcpPacketConn).Close() blocks.
|
||||
for _, conn := range removedConns {
|
||||
m.closeAndLogError(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TCPMuxDefault) getConn(ufrag string, isIPv6 bool, local net.IP) (val *tcpPacketConn, ok bool) {
|
||||
var conns map[ipAddr]*tcpPacketConn
|
||||
if isIPv6 {
|
||||
conns, ok = m.connsIPv6[ufrag]
|
||||
} else {
|
||||
conns, ok = m.connsIPv4[ufrag]
|
||||
}
|
||||
if conns != nil {
|
||||
val, ok = conns[ipAddr(local.String())]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const streamingPacketHeaderLen = 2
|
||||
|
||||
// readStreamingPacket reads 1 packet from stream
|
||||
// read packet bytes https://tools.ietf.org/html/rfc4571#section-2
|
||||
// 2-byte length header prepends each packet:
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// -----------------------------------------------------------------
|
||||
// | LENGTH | RTP or RTCP packet ... |
|
||||
// -----------------------------------------------------------------
|
||||
func readStreamingPacket(conn net.Conn, buf []byte) (int, error) {
|
||||
header := make([]byte, streamingPacketHeaderLen)
|
||||
var bytesRead, n int
|
||||
var err error
|
||||
|
||||
for bytesRead < streamingPacketHeaderLen {
|
||||
if n, err = conn.Read(header[bytesRead:streamingPacketHeaderLen]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bytesRead += n
|
||||
}
|
||||
|
||||
length := int(binary.BigEndian.Uint16(header))
|
||||
|
||||
if length > cap(buf) {
|
||||
return length, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
bytesRead = 0
|
||||
for bytesRead < length {
|
||||
if n, err = conn.Read(buf[bytesRead:length]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bytesRead += n
|
||||
}
|
||||
|
||||
return bytesRead, nil
|
||||
}
|
||||
|
||||
func writeStreamingPacket(conn net.Conn, buf []byte) (int, error) {
|
||||
bufCopy := make([]byte, streamingPacketHeaderLen+len(buf))
|
||||
binary.BigEndian.PutUint16(bufCopy, uint16(len(buf)))
|
||||
copy(bufCopy[2:], buf)
|
||||
|
||||
n, err := conn.Write(bufCopy)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return n - streamingPacketHeaderLen, nil
|
||||
}
|
||||
81
server/vendor/github.com/pion/ice/v2/tcp_mux_multi.go
generated
vendored
Normal file
81
server/vendor/github.com/pion/ice/v2/tcp_mux_multi.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "net"
|
||||
|
||||
// AllConnsGetter allows multiple fixed TCP ports to be used,
|
||||
// each of which is multiplexed like TCPMux. AllConnsGetter also acts as
|
||||
// a TCPMux, in which case it will return a single connection for one
|
||||
// of the ports.
|
||||
type AllConnsGetter interface {
|
||||
GetAllConns(ufrag string, isIPv6 bool, localIP net.IP) ([]net.PacketConn, error)
|
||||
}
|
||||
|
||||
// MultiTCPMuxDefault implements both TCPMux and AllConnsGetter,
|
||||
// allowing users to pass multiple TCPMux instances to the ICE agent
|
||||
// configuration.
|
||||
type MultiTCPMuxDefault struct {
|
||||
muxes []TCPMux
|
||||
}
|
||||
|
||||
// NewMultiTCPMuxDefault creates an instance of MultiTCPMuxDefault that
|
||||
// uses the provided TCPMux instances.
|
||||
func NewMultiTCPMuxDefault(muxes ...TCPMux) *MultiTCPMuxDefault {
|
||||
return &MultiTCPMuxDefault{
|
||||
muxes: muxes,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConnByUfrag returns a PacketConn given the connection's ufrag, network and local address
|
||||
// creates the connection if an existing one can't be found. This, unlike
|
||||
// GetAllConns, will only return a single PacketConn from the first mux that was
|
||||
// passed in to NewMultiTCPMuxDefault.
|
||||
func (m *MultiTCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error) {
|
||||
// NOTE: We always use the first element here in order to maintain the
|
||||
// behavior of using an existing connection if one exists.
|
||||
if len(m.muxes) == 0 {
|
||||
return nil, errNoTCPMuxAvailable
|
||||
}
|
||||
return m.muxes[0].GetConnByUfrag(ufrag, isIPv6, local)
|
||||
}
|
||||
|
||||
// RemoveConnByUfrag stops and removes the muxed packet connection
|
||||
// from all underlying TCPMux instances.
|
||||
func (m *MultiTCPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
||||
for _, mux := range m.muxes {
|
||||
mux.RemoveConnByUfrag(ufrag)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllConns returns a PacketConn for each underlying TCPMux
|
||||
func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP) ([]net.PacketConn, error) {
|
||||
if len(m.muxes) == 0 {
|
||||
// Make sure that we either return at least one connection or an error.
|
||||
return nil, errNoTCPMuxAvailable
|
||||
}
|
||||
var conns []net.PacketConn
|
||||
for _, mux := range m.muxes {
|
||||
conn, err := mux.GetConnByUfrag(ufrag, isIPv6, local)
|
||||
if err != nil {
|
||||
// For now, this implementation is all or none.
|
||||
return nil, err
|
||||
}
|
||||
if conn != nil {
|
||||
conns = append(conns, conn)
|
||||
}
|
||||
}
|
||||
return conns, nil
|
||||
}
|
||||
|
||||
// Close the multi mux, no further connections could be created
|
||||
func (m *MultiTCPMuxDefault) Close() error {
|
||||
var err error
|
||||
for _, mux := range m.muxes {
|
||||
if e := mux.Close(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
324
server/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
generated
vendored
Normal file
324
server/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2/packetio"
|
||||
)
|
||||
|
||||
type bufferedConn struct {
|
||||
net.Conn
|
||||
buf *packetio.Buffer
|
||||
logger logging.LeveledLogger
|
||||
closed int32
|
||||
}
|
||||
|
||||
func newBufferedConn(conn net.Conn, bufSize int, logger logging.LeveledLogger) net.Conn {
|
||||
buf := packetio.NewBuffer()
|
||||
if bufSize > 0 {
|
||||
buf.SetLimitSize(bufSize)
|
||||
}
|
||||
|
||||
bc := &bufferedConn{
|
||||
Conn: conn,
|
||||
buf: buf,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
go bc.writeProcess()
|
||||
return bc
|
||||
}
|
||||
|
||||
func (bc *bufferedConn) Write(b []byte) (int, error) {
|
||||
n, err := bc.buf.Write(b)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (bc *bufferedConn) writeProcess() {
|
||||
pktBuf := make([]byte, receiveMTU)
|
||||
for atomic.LoadInt32(&bc.closed) == 0 {
|
||||
n, err := bc.buf.Read(pktBuf)
|
||||
if errors.Is(err, io.EOF) {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
bc.logger.Warnf("Failed to read from buffer: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := bc.Conn.Write(pktBuf[:n]); err != nil {
|
||||
bc.logger.Warnf("Failed to write: %s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bc *bufferedConn) Close() error {
|
||||
atomic.StoreInt32(&bc.closed, 1)
|
||||
_ = bc.buf.Close()
|
||||
return bc.Conn.Close()
|
||||
}
|
||||
|
||||
type tcpPacketConn struct {
|
||||
params *tcpPacketParams
|
||||
|
||||
// conns is a map of net.Conns indexed by remote net.Addr.String()
|
||||
conns map[string]net.Conn
|
||||
|
||||
recvChan chan streamingPacket
|
||||
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
closedChan chan struct{}
|
||||
closeOnce sync.Once
|
||||
aliveTimer *time.Timer
|
||||
}
|
||||
|
||||
type streamingPacket struct {
|
||||
Data []byte
|
||||
RAddr net.Addr
|
||||
Err error
|
||||
}
|
||||
|
||||
type tcpPacketParams struct {
|
||||
ReadBuffer int
|
||||
LocalAddr net.Addr
|
||||
Logger logging.LeveledLogger
|
||||
WriteBuffer int
|
||||
AliveDuration time.Duration
|
||||
}
|
||||
|
||||
func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn {
|
||||
p := &tcpPacketConn{
|
||||
params: ¶ms,
|
||||
|
||||
conns: map[string]net.Conn{},
|
||||
|
||||
recvChan: make(chan streamingPacket, params.ReadBuffer),
|
||||
closedChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
if params.AliveDuration > 0 {
|
||||
p.aliveTimer = time.AfterFunc(params.AliveDuration, func() {
|
||||
p.params.Logger.Warn("close tcp packet conn by alive timeout")
|
||||
_ = p.Close()
|
||||
})
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) ClearAliveTimer() {
|
||||
t.mu.Lock()
|
||||
if t.aliveTimer != nil {
|
||||
t.aliveTimer.Stop()
|
||||
}
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error {
|
||||
t.params.Logger.Infof("Added connection: %s remote %s to local %s", conn.RemoteAddr().Network(), conn.RemoteAddr(), conn.LocalAddr())
|
||||
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
select {
|
||||
case <-t.closedChan:
|
||||
return io.ErrClosedPipe
|
||||
default:
|
||||
}
|
||||
|
||||
if _, ok := t.conns[conn.RemoteAddr().String()]; ok {
|
||||
return fmt.Errorf("%w: %s", errConnectionAddrAlreadyExist, conn.RemoteAddr().String())
|
||||
}
|
||||
|
||||
if t.params.WriteBuffer > 0 {
|
||||
conn = newBufferedConn(conn, t.params.WriteBuffer, t.params.Logger)
|
||||
}
|
||||
t.conns[conn.RemoteAddr().String()] = conn
|
||||
|
||||
t.wg.Add(1)
|
||||
go func() {
|
||||
defer t.wg.Done()
|
||||
if firstPacketData != nil {
|
||||
select {
|
||||
case <-t.closedChan:
|
||||
// NOTE: recvChan can fill up and never drain in edge
|
||||
// cases while closing a connection, which can cause the
|
||||
// packetConn to never finish closing. Bail out early
|
||||
// here to prevent that.
|
||||
return
|
||||
case t.recvChan <- streamingPacket{firstPacketData, conn.RemoteAddr(), nil}:
|
||||
}
|
||||
}
|
||||
t.startReading(conn)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) startReading(conn net.Conn) {
|
||||
buf := make([]byte, receiveMTU)
|
||||
|
||||
for {
|
||||
n, err := readStreamingPacket(conn, buf)
|
||||
if err != nil {
|
||||
t.params.Logger.Warnf("Failed to read streaming packet: %s", err)
|
||||
t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err})
|
||||
t.removeConn(conn)
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, n)
|
||||
copy(data, buf[:n])
|
||||
|
||||
t.handleRecv(streamingPacket{data, conn.RemoteAddr(), nil})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) handleRecv(pkt streamingPacket) {
|
||||
t.mu.Lock()
|
||||
|
||||
recvChan := t.recvChan
|
||||
if t.isClosed() {
|
||||
recvChan = nil
|
||||
}
|
||||
|
||||
t.mu.Unlock()
|
||||
|
||||
select {
|
||||
case recvChan <- pkt:
|
||||
case <-t.closedChan:
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) isClosed() bool {
|
||||
select {
|
||||
case <-t.closedChan:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo is for passive and s-o candidates.
|
||||
func (t *tcpPacketConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) {
|
||||
pkt, ok := <-t.recvChan
|
||||
|
||||
if !ok {
|
||||
return 0, nil, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
if pkt.Err != nil {
|
||||
return 0, pkt.RAddr, pkt.Err
|
||||
}
|
||||
|
||||
if cap(b) < len(pkt.Data) {
|
||||
return 0, pkt.RAddr, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
n = len(pkt.Data)
|
||||
copy(b, pkt.Data[:n])
|
||||
return n, pkt.RAddr, err
|
||||
}
|
||||
|
||||
// WriteTo is for active and s-o candidates.
|
||||
func (t *tcpPacketConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) {
|
||||
t.mu.Lock()
|
||||
conn, ok := t.conns[rAddr.String()]
|
||||
t.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
n, err = writeStreamingPacket(conn, buf)
|
||||
if err != nil {
|
||||
t.params.Logger.Tracef("%w %s", errWrite, rAddr)
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) closeAndLogError(closer io.Closer) {
|
||||
err := closer.Close()
|
||||
if err != nil {
|
||||
t.params.Logger.Warnf("%v: %s", errClosingConnection, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) removeConn(conn net.Conn) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.closeAndLogError(conn)
|
||||
|
||||
delete(t.conns, conn.RemoteAddr().String())
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) Close() error {
|
||||
t.mu.Lock()
|
||||
|
||||
var shouldCloseRecvChan bool
|
||||
t.closeOnce.Do(func() {
|
||||
close(t.closedChan)
|
||||
shouldCloseRecvChan = true
|
||||
if t.aliveTimer != nil {
|
||||
t.aliveTimer.Stop()
|
||||
}
|
||||
})
|
||||
|
||||
for _, conn := range t.conns {
|
||||
t.closeAndLogError(conn)
|
||||
delete(t.conns, conn.RemoteAddr().String())
|
||||
}
|
||||
|
||||
t.mu.Unlock()
|
||||
|
||||
t.wg.Wait()
|
||||
|
||||
if shouldCloseRecvChan {
|
||||
close(t.recvChan)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) LocalAddr() net.Addr {
|
||||
return t.params.LocalAddr
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) SetDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) SetReadDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) SetWriteDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) CloseChannel() <-chan struct{} {
|
||||
return t.closedChan
|
||||
}
|
||||
|
||||
func (t *tcpPacketConn) String() string {
|
||||
return fmt.Sprintf("tcpPacketConn{LocalAddr: %s}", t.params.LocalAddr)
|
||||
}
|
||||
51
server/vendor/github.com/pion/ice/v2/tcptype.go
generated
vendored
Normal file
51
server/vendor/github.com/pion/ice/v2/tcptype.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "strings"
|
||||
|
||||
// TCPType is the type of ICE TCP candidate as described in
|
||||
// https://tools.ietf.org/html/rfc6544#section-4.5
|
||||
type TCPType int
|
||||
|
||||
const (
|
||||
// TCPTypeUnspecified is the default value. For example UDP candidates do not
|
||||
// need this field.
|
||||
TCPTypeUnspecified TCPType = iota
|
||||
// TCPTypeActive is active TCP candidate, which initiates TCP connections.
|
||||
TCPTypeActive
|
||||
// TCPTypePassive is passive TCP candidate, only accepts TCP connections.
|
||||
TCPTypePassive
|
||||
// TCPTypeSimultaneousOpen is like active and passive at the same time.
|
||||
TCPTypeSimultaneousOpen
|
||||
)
|
||||
|
||||
// NewTCPType creates a new TCPType from string.
|
||||
func NewTCPType(value string) TCPType {
|
||||
switch strings.ToLower(value) {
|
||||
case "active":
|
||||
return TCPTypeActive
|
||||
case "passive":
|
||||
return TCPTypePassive
|
||||
case "so":
|
||||
return TCPTypeSimultaneousOpen
|
||||
default:
|
||||
return TCPTypeUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func (t TCPType) String() string {
|
||||
switch t {
|
||||
case TCPTypeUnspecified:
|
||||
return ""
|
||||
case TCPTypeActive:
|
||||
return "active"
|
||||
case TCPTypePassive:
|
||||
return "passive"
|
||||
case TCPTypeSimultaneousOpen:
|
||||
return "so"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
79
server/vendor/github.com/pion/ice/v2/test_utils.go
generated
vendored
Normal file
79
server/vendor/github.com/pion/ice/v2/test_utils.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newHostRemote(t *testing.T) *CandidateHost {
|
||||
remoteHostConfig := &CandidateHostConfig{
|
||||
Network: "udp",
|
||||
Address: "1.2.3.5",
|
||||
Port: 12350,
|
||||
Component: 1,
|
||||
}
|
||||
hostRemote, err := NewCandidateHost(remoteHostConfig)
|
||||
require.NoError(t, err)
|
||||
return hostRemote
|
||||
}
|
||||
|
||||
func newPrflxRemote(t *testing.T) *CandidatePeerReflexive {
|
||||
prflxConfig := &CandidatePeerReflexiveConfig{
|
||||
Network: "udp",
|
||||
Address: "10.10.10.2",
|
||||
Port: 19217,
|
||||
Component: 1,
|
||||
RelAddr: "4.3.2.1",
|
||||
RelPort: 43211,
|
||||
}
|
||||
prflxRemote, err := NewCandidatePeerReflexive(prflxConfig)
|
||||
require.NoError(t, err)
|
||||
return prflxRemote
|
||||
}
|
||||
|
||||
func newSrflxRemote(t *testing.T) *CandidateServerReflexive {
|
||||
srflxConfig := &CandidateServerReflexiveConfig{
|
||||
Network: "udp",
|
||||
Address: "10.10.10.2",
|
||||
Port: 19218,
|
||||
Component: 1,
|
||||
RelAddr: "4.3.2.1",
|
||||
RelPort: 43212,
|
||||
}
|
||||
srflxRemote, err := NewCandidateServerReflexive(srflxConfig)
|
||||
require.NoError(t, err)
|
||||
return srflxRemote
|
||||
}
|
||||
|
||||
func newRelayRemote(t *testing.T) *CandidateRelay {
|
||||
relayConfig := &CandidateRelayConfig{
|
||||
Network: "udp",
|
||||
Address: "1.2.3.4",
|
||||
Port: 12340,
|
||||
Component: 1,
|
||||
RelAddr: "4.3.2.1",
|
||||
RelPort: 43210,
|
||||
}
|
||||
relayRemote, err := NewCandidateRelay(relayConfig)
|
||||
require.NoError(t, err)
|
||||
return relayRemote
|
||||
}
|
||||
|
||||
func newHostLocal(t *testing.T) *CandidateHost {
|
||||
localHostConfig := &CandidateHostConfig{
|
||||
Network: "udp",
|
||||
Address: "192.168.1.1",
|
||||
Port: 19216,
|
||||
Component: 1,
|
||||
}
|
||||
hostLocal, err := NewCandidateHost(localHostConfig)
|
||||
require.NoError(t, err)
|
||||
return hostLocal
|
||||
}
|
||||
148
server/vendor/github.com/pion/ice/v2/transport.go
generated
vendored
Normal file
148
server/vendor/github.com/pion/ice/v2/transport.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
// Dial connects to the remote agent, acting as the controlling ice agent.
|
||||
// Dial blocks until at least one ice candidate pair has successfully connected.
|
||||
func (a *Agent) Dial(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
|
||||
return a.connect(ctx, true, remoteUfrag, remotePwd)
|
||||
}
|
||||
|
||||
// Accept connects to the remote agent, acting as the controlled ice agent.
|
||||
// Accept blocks until at least one ice candidate pair has successfully connected.
|
||||
func (a *Agent) Accept(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
|
||||
return a.connect(ctx, false, remoteUfrag, remotePwd)
|
||||
}
|
||||
|
||||
// Conn represents the ICE connection.
|
||||
// At the moment the lifetime of the Conn is equal to the Agent.
|
||||
type Conn struct {
|
||||
bytesReceived uint64
|
||||
bytesSent uint64
|
||||
agent *Agent
|
||||
}
|
||||
|
||||
// BytesSent returns the number of bytes sent
|
||||
func (c *Conn) BytesSent() uint64 {
|
||||
return atomic.LoadUint64(&c.bytesSent)
|
||||
}
|
||||
|
||||
// BytesReceived returns the number of bytes received
|
||||
func (c *Conn) BytesReceived() uint64 {
|
||||
return atomic.LoadUint64(&c.bytesReceived)
|
||||
}
|
||||
|
||||
func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) {
|
||||
err := a.ok()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = a.startConnectivityChecks(isControlling, remoteUfrag, remotePwd) //nolint:contextcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Block until pair selected
|
||||
select {
|
||||
case <-a.done:
|
||||
return nil, a.getErr()
|
||||
case <-ctx.Done():
|
||||
return nil, ErrCanceledByCaller
|
||||
case <-a.onConnected:
|
||||
}
|
||||
|
||||
return &Conn{
|
||||
agent: a,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read implements the Conn Read method.
|
||||
func (c *Conn) Read(p []byte) (int, error) {
|
||||
err := c.agent.ok()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := c.agent.buf.Read(p)
|
||||
atomic.AddUint64(&c.bytesReceived, uint64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write implements the Conn Write method.
|
||||
func (c *Conn) Write(p []byte) (int, error) {
|
||||
err := c.agent.ok()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if stun.IsMessage(p) {
|
||||
return 0, errWriteSTUNMessageToIceConn
|
||||
}
|
||||
|
||||
pair := c.agent.getSelectedPair()
|
||||
if pair == nil {
|
||||
if err = c.agent.run(c.agent.context(), func(ctx context.Context, a *Agent) {
|
||||
pair = a.getBestValidCandidatePair()
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if pair == nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
atomic.AddUint64(&c.bytesSent, uint64(len(p)))
|
||||
return pair.Write(p)
|
||||
}
|
||||
|
||||
// Close implements the Conn Close method. It is used to close
|
||||
// the connection. Any calls to Read and Write will be unblocked and return an error.
|
||||
func (c *Conn) Close() error {
|
||||
return c.agent.Close()
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of the current selected pair or nil if there is none.
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
pair := c.agent.getSelectedPair()
|
||||
if pair == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return pair.Local.addr()
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of the current selected pair or nil if there is none.
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
pair := c.agent.getSelectedPair()
|
||||
if pair == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return pair.Remote.addr()
|
||||
}
|
||||
|
||||
// SetDeadline is a stub
|
||||
func (c *Conn) SetDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline is a stub
|
||||
func (c *Conn) SetReadDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline is a stub
|
||||
func (c *Conn) SetWriteDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
368
server/vendor/github.com/pion/ice/v2/udp_mux.go
generated
vendored
Normal file
368
server/vendor/github.com/pion/ice/v2/udp_mux.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
)
|
||||
|
||||
// UDPMux allows multiple connections to go over a single UDP port
|
||||
type UDPMux interface {
|
||||
io.Closer
|
||||
GetConn(ufrag string, addr net.Addr) (net.PacketConn, error)
|
||||
RemoveConnByUfrag(ufrag string)
|
||||
GetListenAddresses() []net.Addr
|
||||
}
|
||||
|
||||
// UDPMuxDefault is an implementation of the interface
|
||||
type UDPMuxDefault struct {
|
||||
params UDPMuxParams
|
||||
|
||||
closedChan chan struct{}
|
||||
closeOnce sync.Once
|
||||
|
||||
// connsIPv4 and connsIPv6 are maps of all udpMuxedConn indexed by ufrag|network|candidateType
|
||||
connsIPv4, connsIPv6 map[string]*udpMuxedConn
|
||||
|
||||
addressMapMu sync.RWMutex
|
||||
addressMap map[udpMuxedConnAddr]*udpMuxedConn
|
||||
|
||||
// Buffer pool to recycle buffers for net.UDPAddr encodes/decodes
|
||||
pool *sync.Pool
|
||||
|
||||
mu sync.Mutex
|
||||
|
||||
// For UDP connection listen at unspecified address
|
||||
localAddrsForUnspecified []net.Addr
|
||||
}
|
||||
|
||||
// UDPMuxParams are parameters for UDPMux.
|
||||
type UDPMuxParams struct {
|
||||
Logger logging.LeveledLogger
|
||||
UDPConn net.PacketConn
|
||||
|
||||
// Required for gathering local addresses
|
||||
// in case a un UDPConn is passed which does not
|
||||
// bind to a specific local address.
|
||||
Net transport.Net
|
||||
}
|
||||
|
||||
// NewUDPMuxDefault creates an implementation of UDPMux
|
||||
func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault {
|
||||
if params.Logger == nil {
|
||||
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
|
||||
}
|
||||
|
||||
var localAddrsForUnspecified []net.Addr
|
||||
if addr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok {
|
||||
params.Logger.Errorf("LocalAddr is not a net.UDPAddr, got %T", params.UDPConn.LocalAddr())
|
||||
} else if ok && addr.IP.IsUnspecified() {
|
||||
// For unspecified addresses, the correct behavior is to return errListenUnspecified, but
|
||||
// it will break the applications that are already using unspecified UDP connection
|
||||
// with UDPMuxDefault, so print a warn log and create a local address list for mux.
|
||||
params.Logger.Warn("UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead")
|
||||
var networks []NetworkType
|
||||
switch {
|
||||
case addr.IP.To4() != nil:
|
||||
networks = []NetworkType{NetworkTypeUDP4}
|
||||
|
||||
case addr.IP.To16() != nil:
|
||||
networks = []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}
|
||||
|
||||
default:
|
||||
params.Logger.Errorf("LocalAddr expected IPV4 or IPV6, got %T", params.UDPConn.LocalAddr())
|
||||
}
|
||||
if len(networks) > 0 {
|
||||
if params.Net == nil {
|
||||
var err error
|
||||
if params.Net, err = stdnet.NewNet(); err != nil {
|
||||
params.Logger.Errorf("Failed to get create network: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ips, err := localInterfaces(params.Net, nil, nil, networks, true)
|
||||
if err == nil {
|
||||
for _, ip := range ips {
|
||||
localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{IP: ip, Port: addr.Port})
|
||||
}
|
||||
} else {
|
||||
params.Logger.Errorf("Failed to get local interfaces for unspecified addr: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := &UDPMuxDefault{
|
||||
addressMap: map[udpMuxedConnAddr]*udpMuxedConn{},
|
||||
params: params,
|
||||
connsIPv4: make(map[string]*udpMuxedConn),
|
||||
connsIPv6: make(map[string]*udpMuxedConn),
|
||||
closedChan: make(chan struct{}, 1),
|
||||
pool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
// Big enough buffer to fit both packet and address
|
||||
return newBufferHolder(receiveMTU)
|
||||
},
|
||||
},
|
||||
localAddrsForUnspecified: localAddrsForUnspecified,
|
||||
}
|
||||
|
||||
go m.connWorker()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// LocalAddr returns the listening address of this UDPMuxDefault
|
||||
func (m *UDPMuxDefault) LocalAddr() net.Addr {
|
||||
return m.params.UDPConn.LocalAddr()
|
||||
}
|
||||
|
||||
// GetListenAddresses returns the list of addresses that this mux is listening on
|
||||
func (m *UDPMuxDefault) GetListenAddresses() []net.Addr {
|
||||
if len(m.localAddrsForUnspecified) > 0 {
|
||||
return m.localAddrsForUnspecified
|
||||
}
|
||||
|
||||
return []net.Addr{m.LocalAddr()}
|
||||
}
|
||||
|
||||
// GetConn returns a PacketConn given the connection's ufrag and network address
|
||||
// creates the connection if an existing one can't be found
|
||||
func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
|
||||
// don't check addr for mux using unspecified address
|
||||
if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() {
|
||||
return nil, errInvalidAddress
|
||||
}
|
||||
|
||||
var isIPv6 bool
|
||||
if udpAddr, _ := addr.(*net.UDPAddr); udpAddr != nil && udpAddr.IP.To4() == nil {
|
||||
isIPv6 = true
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.IsClosed() {
|
||||
return nil, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
if conn, ok := m.getConn(ufrag, isIPv6); ok {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
c := m.createMuxedConn(ufrag)
|
||||
go func() {
|
||||
<-c.CloseChannel()
|
||||
m.RemoveConnByUfrag(ufrag)
|
||||
}()
|
||||
|
||||
if isIPv6 {
|
||||
m.connsIPv6[ufrag] = c
|
||||
} else {
|
||||
m.connsIPv4[ufrag] = c
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// RemoveConnByUfrag stops and removes the muxed packet connection
|
||||
func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
||||
removedConns := make([]*udpMuxedConn, 0, 2)
|
||||
|
||||
// Keep lock section small to avoid deadlock with conn lock
|
||||
m.mu.Lock()
|
||||
if c, ok := m.connsIPv4[ufrag]; ok {
|
||||
delete(m.connsIPv4, ufrag)
|
||||
removedConns = append(removedConns, c)
|
||||
}
|
||||
if c, ok := m.connsIPv6[ufrag]; ok {
|
||||
delete(m.connsIPv6, ufrag)
|
||||
removedConns = append(removedConns, c)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
|
||||
if len(removedConns) == 0 {
|
||||
// No need to lock if no connection was found
|
||||
return
|
||||
}
|
||||
|
||||
m.addressMapMu.Lock()
|
||||
defer m.addressMapMu.Unlock()
|
||||
|
||||
for _, c := range removedConns {
|
||||
addresses := c.getAddresses()
|
||||
for _, addr := range addresses {
|
||||
delete(m.addressMap, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsClosed returns true if the mux had been closed
|
||||
func (m *UDPMuxDefault) IsClosed() bool {
|
||||
select {
|
||||
case <-m.closedChan:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Close the mux, no further connections could be created
|
||||
func (m *UDPMuxDefault) Close() error {
|
||||
var err error
|
||||
m.closeOnce.Do(func() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, c := range m.connsIPv4 {
|
||||
_ = c.Close()
|
||||
}
|
||||
for _, c := range m.connsIPv6 {
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
m.connsIPv4 = make(map[string]*udpMuxedConn)
|
||||
m.connsIPv6 = make(map[string]*udpMuxedConn)
|
||||
|
||||
close(m.closedChan)
|
||||
|
||||
_ = m.params.UDPConn.Close()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) writeTo(buf []byte, rAddr net.Addr) (n int, err error) {
|
||||
return m.params.UDPConn.WriteTo(buf, rAddr)
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr udpMuxedConnAddr) {
|
||||
if m.IsClosed() {
|
||||
return
|
||||
}
|
||||
|
||||
m.addressMapMu.Lock()
|
||||
defer m.addressMapMu.Unlock()
|
||||
|
||||
existing, ok := m.addressMap[addr]
|
||||
if ok {
|
||||
existing.removeAddress(addr)
|
||||
}
|
||||
m.addressMap[addr] = conn
|
||||
|
||||
m.params.Logger.Debugf("Registered %s for %s", addr, conn.params.Key)
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) createMuxedConn(key string) *udpMuxedConn {
|
||||
c := newUDPMuxedConn(&udpMuxedConnParams{
|
||||
Mux: m,
|
||||
Key: key,
|
||||
AddrPool: m.pool,
|
||||
LocalAddr: m.LocalAddr(),
|
||||
Logger: m.params.Logger,
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) connWorker() {
|
||||
logger := m.params.Logger
|
||||
|
||||
defer func() {
|
||||
_ = m.Close()
|
||||
}()
|
||||
|
||||
buf := make([]byte, receiveMTU)
|
||||
for {
|
||||
n, addr, err := m.params.UDPConn.ReadFrom(buf)
|
||||
if m.IsClosed() {
|
||||
return
|
||||
} else if err != nil {
|
||||
if os.IsTimeout(err) {
|
||||
continue
|
||||
} else if !errors.Is(err, io.EOF) {
|
||||
logger.Errorf("Failed to read UDP packet: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
logger.Errorf("Underlying PacketConn did not return a UDPAddr")
|
||||
return
|
||||
}
|
||||
|
||||
// If we have already seen this address dispatch to the appropriate destination
|
||||
m.addressMapMu.Lock()
|
||||
destinationConn := m.addressMap[newUDPMuxedConnAddr(udpAddr)]
|
||||
m.addressMapMu.Unlock()
|
||||
|
||||
// If we haven't seen this address before but is a STUN packet lookup by ufrag
|
||||
if destinationConn == nil && stun.IsMessage(buf[:n]) {
|
||||
msg := &stun.Message{
|
||||
Raw: append([]byte{}, buf[:n]...),
|
||||
}
|
||||
|
||||
if err = msg.Decode(); err != nil {
|
||||
m.params.Logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
attr, stunAttrErr := msg.Get(stun.AttrUsername)
|
||||
if stunAttrErr != nil {
|
||||
m.params.Logger.Warnf("No Username attribute in STUN message from %s", addr.String())
|
||||
continue
|
||||
}
|
||||
|
||||
ufrag := strings.Split(string(attr), ":")[0]
|
||||
isIPv6 := udpAddr.IP.To4() == nil
|
||||
|
||||
m.mu.Lock()
|
||||
destinationConn, _ = m.getConn(ufrag, isIPv6)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
if destinationConn == nil {
|
||||
m.params.Logger.Tracef("Dropping packet from %s, addr: %s", udpAddr, addr)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = destinationConn.writePacket(buf[:n], udpAddr); err != nil {
|
||||
m.params.Logger.Errorf("Failed to write packet: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *UDPMuxDefault) getConn(ufrag string, isIPv6 bool) (val *udpMuxedConn, ok bool) {
|
||||
if isIPv6 {
|
||||
val, ok = m.connsIPv6[ufrag]
|
||||
} else {
|
||||
val, ok = m.connsIPv4[ufrag]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type bufferHolder struct {
|
||||
next *bufferHolder
|
||||
buf []byte
|
||||
addr *net.UDPAddr
|
||||
}
|
||||
|
||||
func newBufferHolder(size int) *bufferHolder {
|
||||
return &bufferHolder{
|
||||
buf: make([]byte, size),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bufferHolder) reset() {
|
||||
b.next = nil
|
||||
b.addr = nil
|
||||
}
|
||||
228
server/vendor/github.com/pion/ice/v2/udp_mux_multi.go
generated
vendored
Normal file
228
server/vendor/github.com/pion/ice/v2/udp_mux_multi.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
)
|
||||
|
||||
// MultiUDPMuxDefault implements both UDPMux and AllConnsGetter,
|
||||
// allowing users to pass multiple UDPMux instances to the ICE agent
|
||||
// configuration.
|
||||
type MultiUDPMuxDefault struct {
|
||||
muxes []UDPMux
|
||||
localAddrToMux map[string]UDPMux
|
||||
}
|
||||
|
||||
// NewMultiUDPMuxDefault creates an instance of MultiUDPMuxDefault that
|
||||
// uses the provided UDPMux instances.
|
||||
func NewMultiUDPMuxDefault(muxes ...UDPMux) *MultiUDPMuxDefault {
|
||||
addrToMux := make(map[string]UDPMux)
|
||||
for _, mux := range muxes {
|
||||
for _, addr := range mux.GetListenAddresses() {
|
||||
addrToMux[addr.String()] = mux
|
||||
}
|
||||
}
|
||||
return &MultiUDPMuxDefault{
|
||||
muxes: muxes,
|
||||
localAddrToMux: addrToMux,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConn returns a PacketConn given the connection's ufrag and network
|
||||
// creates the connection if an existing one can't be found.
|
||||
func (m *MultiUDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
|
||||
mux, ok := m.localAddrToMux[addr.String()]
|
||||
if !ok {
|
||||
return nil, errNoUDPMuxAvailable
|
||||
}
|
||||
return mux.GetConn(ufrag, addr)
|
||||
}
|
||||
|
||||
// RemoveConnByUfrag stops and removes the muxed packet connection
|
||||
// from all underlying UDPMux instances.
|
||||
func (m *MultiUDPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
||||
for _, mux := range m.muxes {
|
||||
mux.RemoveConnByUfrag(ufrag)
|
||||
}
|
||||
}
|
||||
|
||||
// Close the multi mux, no further connections could be created
|
||||
func (m *MultiUDPMuxDefault) Close() error {
|
||||
var err error
|
||||
for _, mux := range m.muxes {
|
||||
if e := mux.Close(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetListenAddresses returns the list of addresses that this mux is listening on
|
||||
func (m *MultiUDPMuxDefault) GetListenAddresses() []net.Addr {
|
||||
addrs := make([]net.Addr, 0, len(m.localAddrToMux))
|
||||
for _, mux := range m.muxes {
|
||||
addrs = append(addrs, mux.GetListenAddresses()...)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// NewMultiUDPMuxFromPort creates an instance of MultiUDPMuxDefault that
|
||||
// listen all interfaces on the provided port.
|
||||
func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) {
|
||||
params := multiUDPMuxFromPortParam{
|
||||
networks: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.apply(¶ms)
|
||||
}
|
||||
|
||||
if params.net == nil {
|
||||
var err error
|
||||
if params.net, err = stdnet.NewNet(); err != nil {
|
||||
return nil, fmt.Errorf("failed to get create network: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
ips, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conns := make([]net.PacketConn, 0, len(ips))
|
||||
for _, ip := range ips {
|
||||
conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{IP: ip, Port: port})
|
||||
if listenErr != nil {
|
||||
err = listenErr
|
||||
break
|
||||
}
|
||||
if params.readBufferSize > 0 {
|
||||
_ = conn.SetReadBuffer(params.readBufferSize)
|
||||
}
|
||||
if params.writeBufferSize > 0 {
|
||||
_ = conn.SetWriteBuffer(params.writeBufferSize)
|
||||
}
|
||||
conns = append(conns, conn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
for _, conn := range conns {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
muxes := make([]UDPMux, 0, len(conns))
|
||||
for _, conn := range conns {
|
||||
mux := NewUDPMuxDefault(UDPMuxParams{
|
||||
Logger: params.logger,
|
||||
UDPConn: conn,
|
||||
Net: params.net,
|
||||
})
|
||||
muxes = append(muxes, mux)
|
||||
}
|
||||
|
||||
return NewMultiUDPMuxDefault(muxes...), nil
|
||||
}
|
||||
|
||||
// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort
|
||||
type UDPMuxFromPortOption interface {
|
||||
apply(*multiUDPMuxFromPortParam)
|
||||
}
|
||||
|
||||
type multiUDPMuxFromPortParam struct {
|
||||
ifFilter func(string) bool
|
||||
ipFilter func(ip net.IP) bool
|
||||
networks []NetworkType
|
||||
readBufferSize int
|
||||
writeBufferSize int
|
||||
logger logging.LeveledLogger
|
||||
includeLoopback bool
|
||||
net transport.Net
|
||||
}
|
||||
|
||||
type udpMuxFromPortOption struct {
|
||||
f func(*multiUDPMuxFromPortParam)
|
||||
}
|
||||
|
||||
func (o *udpMuxFromPortOption) apply(p *multiUDPMuxFromPortParam) {
|
||||
o.f(p)
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used
|
||||
func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.ifFilter = f
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used
|
||||
func UDPMuxFromPortWithIPFilter(f func(ip net.IP) bool) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.ipFilter = f
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6
|
||||
func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.networks = networks
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size
|
||||
func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.readBufferSize = size
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size
|
||||
func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.writeBufferSize = size
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithLogger set the logger for the created UDPMux
|
||||
func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.logger = logger
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithLoopback set loopback interface should be included
|
||||
func UDPMuxFromPortWithLoopback() UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.includeLoopback = true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UDPMuxFromPortWithNet sets the network transport to use.
|
||||
func UDPMuxFromPortWithNet(n transport.Net) UDPMuxFromPortOption {
|
||||
return &udpMuxFromPortOption{
|
||||
f: func(p *multiUDPMuxFromPortParam) {
|
||||
p.net = n
|
||||
},
|
||||
}
|
||||
}
|
||||
271
server/vendor/github.com/pion/ice/v2/udp_mux_universal.go
generated
vendored
Normal file
271
server/vendor/github.com/pion/ice/v2/udp_mux_universal.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/stun"
|
||||
"github.com/pion/transport/v2"
|
||||
)
|
||||
|
||||
// UniversalUDPMux allows multiple connections to go over a single UDP port for
|
||||
// host, server reflexive and relayed candidates.
|
||||
// Actual connection muxing is happening in the UDPMux.
|
||||
type UniversalUDPMux interface {
|
||||
UDPMux
|
||||
GetXORMappedAddr(stunAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error)
|
||||
GetRelayedAddr(turnAddr net.Addr, deadline time.Duration) (*net.Addr, error)
|
||||
GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
// UniversalUDPMuxDefault handles STUN and TURN servers packets by wrapping the original UDPConn overriding ReadFrom.
|
||||
// It the passes packets to the UDPMux that does the actual connection muxing.
|
||||
type UniversalUDPMuxDefault struct {
|
||||
*UDPMuxDefault
|
||||
params UniversalUDPMuxParams
|
||||
|
||||
// Since we have a shared socket, for srflx candidates it makes sense to have a shared mapped address across all the agents
|
||||
// stun.XORMappedAddress indexed by the STUN server addr
|
||||
xorMappedMap map[string]*xorMapped
|
||||
}
|
||||
|
||||
// UniversalUDPMuxParams are parameters for UniversalUDPMux server reflexive.
|
||||
type UniversalUDPMuxParams struct {
|
||||
Logger logging.LeveledLogger
|
||||
UDPConn net.PacketConn
|
||||
XORMappedAddrCacheTTL time.Duration
|
||||
Net transport.Net
|
||||
}
|
||||
|
||||
// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux
|
||||
func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDefault {
|
||||
if params.Logger == nil {
|
||||
params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
|
||||
}
|
||||
if params.XORMappedAddrCacheTTL == 0 {
|
||||
params.XORMappedAddrCacheTTL = time.Second * 25
|
||||
}
|
||||
|
||||
m := &UniversalUDPMuxDefault{
|
||||
params: params,
|
||||
xorMappedMap: make(map[string]*xorMapped),
|
||||
}
|
||||
|
||||
// Wrap UDP connection, process server reflexive messages
|
||||
// before they are passed to the UDPMux connection handler (connWorker)
|
||||
m.params.UDPConn = &udpConn{
|
||||
PacketConn: params.UDPConn,
|
||||
mux: m,
|
||||
logger: params.Logger,
|
||||
}
|
||||
|
||||
// Embed UDPMux
|
||||
udpMuxParams := UDPMuxParams{
|
||||
Logger: params.Logger,
|
||||
UDPConn: m.params.UDPConn,
|
||||
Net: m.params.Net,
|
||||
}
|
||||
m.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets
|
||||
type udpConn struct {
|
||||
net.PacketConn
|
||||
mux *UniversalUDPMuxDefault
|
||||
logger logging.LeveledLogger
|
||||
}
|
||||
|
||||
// GetRelayedAddr creates relayed connection to the given TURN service and returns the relayed addr.
|
||||
// Not implemented yet.
|
||||
func (m *UniversalUDPMuxDefault) GetRelayedAddr(net.Addr, time.Duration) (*net.Addr, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL (e.g. STUN URL) to be able to support multiple STUN/TURN servers
|
||||
// and return a unique connection per server.
|
||||
func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error) {
|
||||
return m.UDPMuxDefault.GetConn(fmt.Sprintf("%s%s", ufrag, url), addr)
|
||||
}
|
||||
|
||||
// ReadFrom is called by UDPMux connWorker and handles packets coming from the STUN server discovering a mapped address.
|
||||
// It passes processed packets further to the UDPMux (maybe this is not really necessary).
|
||||
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, addr, err = c.PacketConn.ReadFrom(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if stun.IsMessage(p[:n]) {
|
||||
msg := &stun.Message{
|
||||
Raw: append([]byte{}, p[:n]...),
|
||||
}
|
||||
|
||||
if err = msg.Decode(); err != nil {
|
||||
c.logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err)
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
// Message about this err will be logged in the UDPMux
|
||||
return
|
||||
}
|
||||
|
||||
if c.mux.isXORMappedResponse(msg, udpAddr.String()) {
|
||||
err = c.mux.handleXORMappedResponse(udpAddr, msg)
|
||||
if err != nil {
|
||||
c.logger.Debugf("%w: %v", errGetXorMappedAddrResponse, err)
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return n, addr, err
|
||||
}
|
||||
|
||||
// isXORMappedResponse indicates whether the message is a XORMappedAddress and is coming from the known STUN server.
|
||||
func (m *UniversalUDPMuxDefault) isXORMappedResponse(msg *stun.Message, stunAddr string) bool {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
// Check first if it is a STUN server address because remote peer can also send similar messages but as a BindingSuccess
|
||||
_, ok := m.xorMappedMap[stunAddr]
|
||||
_, err := msg.Get(stun.AttrXORMappedAddress)
|
||||
return err == nil && ok
|
||||
}
|
||||
|
||||
// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute
|
||||
// and set the mapped address for the server
|
||||
func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr, msg *stun.Message) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
mappedAddr, ok := m.xorMappedMap[stunAddr.String()]
|
||||
if !ok {
|
||||
return errNoXorAddrMapping
|
||||
}
|
||||
|
||||
var addr stun.XORMappedAddress
|
||||
if err := addr.GetFrom(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.xorMappedMap[stunAddr.String()] = mappedAddr
|
||||
mappedAddr.SetAddr(&addr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetXORMappedAddr returns *stun.XORMappedAddress if already present for a given STUN server.
|
||||
// Makes a STUN binding request to discover mapped address otherwise.
|
||||
// Blocks until the stun.XORMappedAddress has been discovered or deadline.
|
||||
// Method is safe for concurrent use.
|
||||
func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) {
|
||||
m.mu.Lock()
|
||||
mappedAddr, ok := m.xorMappedMap[serverAddr.String()]
|
||||
// If we already have a mapping for this STUN server (address already received)
|
||||
// and if it is not too old we return it without making a new request to STUN server
|
||||
if ok {
|
||||
if mappedAddr.expired() {
|
||||
mappedAddr.closeWaiters()
|
||||
delete(m.xorMappedMap, serverAddr.String())
|
||||
ok = false
|
||||
} else if mappedAddr.pending() {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
if ok {
|
||||
return mappedAddr.addr, nil
|
||||
}
|
||||
|
||||
// Otherwise, make a STUN request to discover the address
|
||||
// or wait for already sent request to complete
|
||||
waitAddrReceived, err := m.writeSTUN(serverAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %s", errWriteSTUNMessage, err) //nolint:errorlint
|
||||
}
|
||||
|
||||
// Block until response was handled by the connWorker routine and XORMappedAddress was updated
|
||||
select {
|
||||
case <-waitAddrReceived:
|
||||
// When channel closed, addr was obtained
|
||||
m.mu.Lock()
|
||||
mappedAddr := *m.xorMappedMap[serverAddr.String()]
|
||||
m.mu.Unlock()
|
||||
if mappedAddr.addr == nil {
|
||||
return nil, errNoXorAddrMapping
|
||||
}
|
||||
return mappedAddr.addr, nil
|
||||
case <-time.After(deadline):
|
||||
return nil, errXORMappedAddrTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// writeSTUN sends a STUN request via UDP conn.
|
||||
//
|
||||
// The returned channel is closed when the STUN response has been received.
|
||||
// Method is safe for concurrent use.
|
||||
func (m *UniversalUDPMuxDefault) writeSTUN(serverAddr net.Addr) (chan struct{}, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// If record present in the map, we already sent a STUN request,
|
||||
// just wait when waitAddrReceived will be closed
|
||||
addrMap, ok := m.xorMappedMap[serverAddr.String()]
|
||||
if !ok {
|
||||
addrMap = &xorMapped{
|
||||
expiresAt: time.Now().Add(m.params.XORMappedAddrCacheTTL),
|
||||
waitAddrReceived: make(chan struct{}),
|
||||
}
|
||||
m.xorMappedMap[serverAddr.String()] = addrMap
|
||||
}
|
||||
|
||||
req, err := stun.Build(stun.BindingRequest, stun.TransactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = m.params.UDPConn.WriteTo(req.Raw, serverAddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return addrMap.waitAddrReceived, nil
|
||||
}
|
||||
|
||||
type xorMapped struct {
|
||||
addr *stun.XORMappedAddress
|
||||
waitAddrReceived chan struct{}
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
func (a *xorMapped) closeWaiters() {
|
||||
select {
|
||||
case <-a.waitAddrReceived:
|
||||
// Notify was close, ok, that means we received duplicate response just exit
|
||||
break
|
||||
default:
|
||||
// Notify tha twe have a new addr
|
||||
close(a.waitAddrReceived)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *xorMapped) pending() bool {
|
||||
return a.addr == nil
|
||||
}
|
||||
|
||||
func (a *xorMapped) expired() bool {
|
||||
return a.expiresAt.Before(time.Now())
|
||||
}
|
||||
|
||||
func (a *xorMapped) SetAddr(addr *stun.XORMappedAddress) {
|
||||
a.addr = addr
|
||||
a.closeWaiters()
|
||||
}
|
||||
248
server/vendor/github.com/pion/ice/v2/udp_muxed_conn.go
generated
vendored
Normal file
248
server/vendor/github.com/pion/ice/v2/udp_muxed_conn.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
type udpMuxedConnState int
|
||||
|
||||
const (
|
||||
udpMuxedConnOpen udpMuxedConnState = iota
|
||||
udpMuxedConnWaiting
|
||||
udpMuxedConnClosed
|
||||
)
|
||||
|
||||
type udpMuxedConnAddr struct {
|
||||
ip [16]byte
|
||||
port uint16
|
||||
}
|
||||
|
||||
func newUDPMuxedConnAddr(addr *net.UDPAddr) (a udpMuxedConnAddr) {
|
||||
copy(a.ip[:], addr.IP.To16())
|
||||
a.port = uint16(addr.Port)
|
||||
return a
|
||||
}
|
||||
|
||||
type udpMuxedConnParams struct {
|
||||
Mux *UDPMuxDefault
|
||||
AddrPool *sync.Pool
|
||||
Key string
|
||||
LocalAddr net.Addr
|
||||
Logger logging.LeveledLogger
|
||||
}
|
||||
|
||||
// udpMuxedConn represents a logical packet conn for a single remote as identified by ufrag
|
||||
type udpMuxedConn struct {
|
||||
params *udpMuxedConnParams
|
||||
// Remote addresses that we have sent to on this conn
|
||||
addresses []udpMuxedConnAddr
|
||||
|
||||
// FIFO queue holding incoming packets
|
||||
bufHead, bufTail *bufferHolder
|
||||
notify chan struct{}
|
||||
closedChan chan struct{}
|
||||
state udpMuxedConnState
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newUDPMuxedConn(params *udpMuxedConnParams) *udpMuxedConn {
|
||||
return &udpMuxedConn{
|
||||
params: params,
|
||||
notify: make(chan struct{}, 1),
|
||||
closedChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) {
|
||||
for {
|
||||
c.mu.Lock()
|
||||
if c.bufTail != nil {
|
||||
pkt := c.bufTail
|
||||
c.bufTail = pkt.next
|
||||
|
||||
if pkt == c.bufHead {
|
||||
c.bufHead = nil
|
||||
}
|
||||
c.mu.Unlock()
|
||||
|
||||
if len(b) < len(pkt.buf) {
|
||||
err = io.ErrShortBuffer
|
||||
} else {
|
||||
n = copy(b, pkt.buf)
|
||||
rAddr = pkt.addr
|
||||
}
|
||||
|
||||
pkt.reset()
|
||||
c.params.AddrPool.Put(pkt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if c.state == udpMuxedConnClosed {
|
||||
c.mu.Unlock()
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
c.state = udpMuxedConnWaiting
|
||||
c.mu.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.notify:
|
||||
case <-c.closedChan:
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) {
|
||||
if c.isClosed() {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
// Each time we write to a new address, we'll register it with the mux
|
||||
addr := newUDPMuxedConnAddr(rAddr.(*net.UDPAddr))
|
||||
if !c.containsAddress(addr) {
|
||||
c.addAddress(addr)
|
||||
}
|
||||
|
||||
return c.params.Mux.writeTo(buf, rAddr)
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) LocalAddr() net.Addr {
|
||||
return c.params.LocalAddr
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) SetDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) SetReadDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) SetWriteDeadline(time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) CloseChannel() <-chan struct{} {
|
||||
return c.closedChan
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.state != udpMuxedConnClosed {
|
||||
for pkt := c.bufTail; pkt != nil; {
|
||||
next := pkt.next
|
||||
|
||||
pkt.reset()
|
||||
c.params.AddrPool.Put(pkt)
|
||||
|
||||
pkt = next
|
||||
}
|
||||
c.bufHead = nil
|
||||
c.bufTail = nil
|
||||
|
||||
c.state = udpMuxedConnClosed
|
||||
close(c.closedChan)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) isClosed() bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.state == udpMuxedConnClosed
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) getAddresses() []udpMuxedConnAddr {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
addresses := make([]udpMuxedConnAddr, len(c.addresses))
|
||||
copy(addresses, c.addresses)
|
||||
return addresses
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) addAddress(addr udpMuxedConnAddr) {
|
||||
c.mu.Lock()
|
||||
c.addresses = append(c.addresses, addr)
|
||||
c.mu.Unlock()
|
||||
|
||||
// Map it on mux
|
||||
c.params.Mux.registerConnForAddress(c, addr)
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) removeAddress(addr udpMuxedConnAddr) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
newAddresses := make([]udpMuxedConnAddr, 0, len(c.addresses))
|
||||
for _, a := range c.addresses {
|
||||
if a != addr {
|
||||
newAddresses = append(newAddresses, a)
|
||||
}
|
||||
}
|
||||
|
||||
c.addresses = newAddresses
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) containsAddress(addr udpMuxedConnAddr) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
for _, a := range c.addresses {
|
||||
if addr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error {
|
||||
pkt := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert
|
||||
if cap(pkt.buf) < len(data) {
|
||||
c.params.AddrPool.Put(pkt)
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
|
||||
pkt.buf = append(pkt.buf[:0], data...)
|
||||
pkt.addr = addr
|
||||
|
||||
c.mu.Lock()
|
||||
if c.state == udpMuxedConnClosed {
|
||||
c.mu.Unlock()
|
||||
|
||||
pkt.reset()
|
||||
c.params.AddrPool.Put(pkt)
|
||||
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
|
||||
if c.bufHead != nil {
|
||||
c.bufHead.next = pkt
|
||||
}
|
||||
c.bufHead = pkt
|
||||
|
||||
if c.bufTail == nil {
|
||||
c.bufTail = pkt
|
||||
}
|
||||
|
||||
state := c.state
|
||||
c.state = udpMuxedConnOpen
|
||||
c.mu.Unlock()
|
||||
|
||||
if state == udpMuxedConnWaiting {
|
||||
select {
|
||||
case c.notify <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
82
server/vendor/github.com/pion/ice/v2/url.go
generated
vendored
Normal file
82
server/vendor/github.com/pion/ice/v2/url.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "github.com/pion/stun"
|
||||
|
||||
type (
|
||||
// URL represents a STUN (rfc7064) or TURN (rfc7065) URI
|
||||
//
|
||||
// Deprecated: Please use pion/stun.URI
|
||||
URL = stun.URI
|
||||
|
||||
// ProtoType indicates the transport protocol type that is used in the ice.URL
|
||||
// structure.
|
||||
//
|
||||
// Deprecated: TPlease use pion/stun.ProtoType
|
||||
ProtoType = stun.ProtoType
|
||||
|
||||
// SchemeType indicates the type of server used in the ice.URL structure.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeType
|
||||
SchemeType = stun.SchemeType
|
||||
)
|
||||
|
||||
const (
|
||||
// SchemeTypeSTUN indicates the URL represents a STUN server.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeTypeSTUN
|
||||
SchemeTypeSTUN = stun.SchemeTypeSTUN
|
||||
|
||||
// SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeTypeSTUNS
|
||||
SchemeTypeSTUNS = stun.SchemeTypeSTUNS
|
||||
|
||||
// SchemeTypeTURN indicates the URL represents a TURN server.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeTypeTURN
|
||||
SchemeTypeTURN = stun.SchemeTypeTURN
|
||||
|
||||
// SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeTypeTURNS
|
||||
SchemeTypeTURNS = stun.SchemeTypeTURNS
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtoTypeUDP indicates the URL uses a UDP transport.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.ProtoTypeUDP
|
||||
ProtoTypeUDP = stun.ProtoTypeUDP
|
||||
|
||||
// ProtoTypeTCP indicates the URL uses a TCP transport.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.ProtoTypeTCP
|
||||
ProtoTypeTCP = stun.ProtoTypeTCP
|
||||
)
|
||||
|
||||
// Unknown represents and unknown ProtoType or SchemeType
|
||||
//
|
||||
// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown
|
||||
const Unknown = 0
|
||||
|
||||
// ParseURL parses a STUN or TURN urls following the ABNF syntax described in
|
||||
// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065
|
||||
// respectively.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.ParseURI
|
||||
var ParseURL = stun.ParseURI //nolint:gochecknoglobals
|
||||
|
||||
// NewSchemeType defines a procedure for creating a new SchemeType from a raw
|
||||
// string naming the scheme type.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.NewSchemeType
|
||||
var NewSchemeType = stun.NewSchemeType //nolint:gochecknoglobals
|
||||
|
||||
// NewProtoType defines a procedure for creating a new ProtoType from a raw
|
||||
// string naming the transport protocol type.
|
||||
//
|
||||
// Deprecated: Please use pion/stun.NewProtoType
|
||||
var NewProtoType = stun.NewProtoType //nolint:gochecknoglobals
|
||||
26
server/vendor/github.com/pion/ice/v2/usecandidate.go
generated
vendored
Normal file
26
server/vendor/github.com/pion/ice/v2/usecandidate.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ice
|
||||
|
||||
import "github.com/pion/stun"
|
||||
|
||||
// UseCandidateAttr represents USE-CANDIDATE attribute.
|
||||
type UseCandidateAttr struct{}
|
||||
|
||||
// AddTo adds USE-CANDIDATE attribute to message.
|
||||
func (UseCandidateAttr) AddTo(m *stun.Message) error {
|
||||
m.Add(stun.AttrUseCandidate, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsSet returns true if USE-CANDIDATE attribute is set.
|
||||
func (UseCandidateAttr) IsSet(m *stun.Message) bool {
|
||||
_, err := m.Get(stun.AttrUseCandidate)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// UseCandidate is shorthand for UseCandidateAttr.
|
||||
func UseCandidate() UseCandidateAttr {
|
||||
return UseCandidateAttr{}
|
||||
}
|
||||
Reference in New Issue
Block a user