1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

TEST: Upgrade pion to v3.2.9. (#3567)

------

Co-authored-by: chundonglinlin <chundonglinlin@163.com>
This commit is contained in:
Winlin 2023-06-05 11:25:04 +08:00 committed by GitHub
parent 104cf14d68
commit df854339ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1383 changed files with 118469 additions and 41421 deletions

View file

@ -1,9 +1,25 @@
### JetBrains IDE ###
#####################
.idea/
### Emacs Temporary Files ###
#############################
*~
### Folders ###
###############
bin/
vendor/
node_modules/
### Files ###
#############
*.ivf
*.ogg
tags
cover.out
*.sw[poe]
examples/turn-client/tcp/tcp
examples/turn-client/udp/udp
examples/turn-server/add-software-attribute/add-software-attribute
examples/turn-server/log/log
examples/turn-server/simple/simple
examples/turn-server/tcp/tcp
examples/lt-cred-generator/lt-cred-generator
examples/turn-server/lt-cred/lt-cred
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

View file

@ -15,14 +15,21 @@ linters-settings:
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
- deadcode # Finds unused code
- 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
- 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
@ -35,43 +42,64 @@ linters:
- 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
- golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
- 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
- scopelint # Scopelint checks for unpinned variables in go programs
- 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
- structcheck # Finds unused struct fields
- 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
- varcheck # Finds unused global variables and constants
- 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
- forbidigo # Forbids identifiers
- 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

View file

@ -1,109 +1,2 @@
before:
hooks:
- go mod tidy
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
builds:
- binary: turn-client-tcp
id: turn-client-tcp
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-client/tcp
- binary: turn-client-udp
id: turn-client-udp
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-client/udp
- binary: turn-server-add-software-attribute
id: turn-server-add-software-attribute
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-server/add-software-attribute
- binary: turn-server-log
id: turn-server-log
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-server/log
- binary: turn-server-simple
id: turn-server-simple
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-server/simple/
- binary: turn-server-tcp
id: turn-server-tcp
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./examples/turn-server/tcp/
- skip: true

View file

@ -0,0 +1,43 @@
# 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>
Aleksandr Razumov <ar@gortc.io>
andrefsp <andrefsp@gmail.com>
Antonio Sorrentino <ansorrenltd@gmail.com>
Atsushi Watanabe <atsushi.w@ieee.org>
backkem <mail@backkem.me>
Caleb Phillips <calebrphillips@gmail.com>
cnderrauber <zengjie9004@gmail.com>
David Colburn <xero73@gmail.com>
Gabor Retvari <retvari@tmit.bme.hu>
Herman Banken <hermanbanken@gmail.com>
Hugo Arregui <hugo.arregui@gmail.com>
Igor German <igor.german@synesis.ru>
Ingmar Wittkau <iwittkau@users.noreply.github.com>
Jannis Mattheis <contact@jmattheis.de>
John Bradley <john@pion.ly>
jose nazario <jose.monkey.org@gmail.com>
Juliusz Chroboczek <jch@irif.fr>
lllf <littlelightlittlefire@gmail.com>
Lukas Rezek <lukas@miratronix.com>
Marouane <6729798+nindolabs@users.noreply.github.com>
Mészáros Mihály <bakfitty@gmail.com>
nindolabs <6729798+nindolabs@users.noreply.github.com>
Onwuka Gideon <dongidomed@gmail.com>
Robert Eperjesi <eperjesi@uber.com>
Sean DuBois <duboisea@twitch.tv>
Sean DuBois <seaduboi@amazon.com>
Sean DuBois <sean@pion.ly>
Sean DuBois <sean@siobud.com>
songjiayang <songjiayang@jd.com>
Steffen Vogel <post@steffenvogel.de>
ted <ted@skydio.com>
Tom Clift <tom@clift.id.au>
Yusuke Nakamura <yusuke1994525@gmail.com>
Yutaka Takeda <yt0916@gmail.com>
# List of contributors not appearing in Git history

View file

@ -0,0 +1,17 @@
## FAQ
Q: Will pion/turn also act as a STUN server?
A: Yes.
Q: How do I implement token-based authentication?
A: Replace the username with a token in the [AuthHandler](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L49).
The password sent by the client can be any non-empty string, as long as it matches that used by the [GenerateAuthKey](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L41)
function.
Q: Will WebRTC prioritize using STUN over TURN?
A: Yes.

View file

@ -13,8 +13,7 @@
<a href="https://travis-ci.org/pion/turn"><img src="https://travis-ci.org/pion/turn.svg?branch=master" alt="Build Status"></a>
<a href="https://pkg.go.dev/github.com/pion/turn/v2"><img src="https://godoc.org/github.com/pion/turn?status.svg" alt="GoDoc"></a>
<a href="https://codecov.io/gh/pion/turn"><img src="https://codecov.io/gh/pion/turn/branch/master/graph/badge.svg" alt="Coverage Status"></a>
<a href="https://goreportcard.com/report/github.com/pion/turn"><img src="https://goreportcard.com/badge/github.com/pion/turn" alt="Go Report Card"></a>
<a href="https://www.codacy.com/app/pion/turn"><img src="https://api.codacy.com/project/badge/Grade/d53ec6c70576476cb16c140c2964afde" alt="Codacy Badge"></a>
<a href="https://goreportcard.com/report/github.com/pion/turn/v2"><img src="https://goreportcard.com/badge/github.com/pion/turn/v2" 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>
@ -64,33 +63,7 @@ Pion has an active community on the [Golang Slack](https://pion.ly/slack). Sign
We are always looking to support **your projects**. Please reach out if you have something to build!
### Contributing
Check out the [CONTRIBUTING.md](CONTRIBUTING.md) to join the group of amazing people making this project possible:
* [Michiel De Backker](https://github.com/backkem) - *Documentation*
* [Ingmar Wittkau](https://github.com/iwittkau) - *STUN client*
* [John Bradley](https://github.com/kc5nra) - *Original Author*
* [jose nazario](https://github.com/paralax) - *Documentation*
* [Mészáros Mihály](https://github.com/misi) - *Documentation*
* [Mike Santry](https://github.com/santrym) - *Mascot*
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
* [winds2016](https://github.com/winds2016) - *Windows platform testing*
* [songjiayang](https://github.com/songjiayang) - *SongJiaYang*
* [Yutaka Takeda](https://github.com/enobufs) - *vnet*
* [namreg](https://github.com/namreg) - *Igor German*
* [Aleksandr Razumov](https://github.com/ernado) - *protocol*
* [Robert Eperjesi](https://github.com/epes)
* [Lukas Rezek](https://github.com/lrezek)
* [Hugo Arregui](https://github.com/hugoArregui)
* [Aaron France](https://github.com/AeroNotix)
* [Atsushi Watanabe](https://github.com/at-wat)
* [Tom Clift](https://github.com/tclift)
* [lllf](https://github.com/LittleLightLittleFire)
* nindolabs (Marouane)
* [Onwuka Gideon](https://github.com/dongido001)
* [Herman Banken](https://github.com/hermanbanken)
* [Jannis Mattheis](https://github.com/jmattheis)
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible.
### License
MIT License - see [LICENSE.md](LICENSE.md) for full text

View file

@ -10,7 +10,9 @@ import (
"github.com/pion/logging"
"github.com/pion/stun"
"github.com/pion/transport/vnet"
"github.com/pion/transport/v2"
"github.com/pion/transport/v2/stdnet"
"github.com/pion/transport/v2/vnet"
"github.com/pion/turn/v2/internal/client"
"github.com/pion/turn/v2/internal/proto"
)
@ -34,7 +36,7 @@ const (
// ClientConfig is a bag of config parameters for Client.
type ClientConfig struct {
STUNServerAddr string // STUN server address (e.g. "stun.abc.com:3478")
TURNServerAddr string // TURN server addrees (e.g. "turn.abc.com:3478")
TURNServerAddr string // TURN server address (e.g. "turn.abc.com:3478")
Username string
Password string
Realm string
@ -42,7 +44,7 @@ type ClientConfig struct {
RTO time.Duration
Conn net.PacketConn // Listening socket (net.PacketConn)
LoggerFactory logging.LoggerFactory
Net *vnet.Net
Net transport.Net
}
// Client is a STUN server client
@ -50,8 +52,8 @@ type Client struct {
conn net.PacketConn // read-only
stunServ net.Addr // read-only
turnServ net.Addr // read-only
stunServStr string // read-only, used for dmuxing
turnServStr string // read-only, used for dmuxing
stunServStr string // read-only, used for de-multiplexing
turnServStr string // read-only, used for de-multiplexing
username stun.Username // read-only
password string // read-only
realm stun.Realm // read-only
@ -62,7 +64,7 @@ type Client struct {
relayedConn *client.UDPConn // protected by mutex ***
allocTryLock client.TryLock // thread-safe
listenTryLock client.TryLock // thread-safe
net *vnet.Net // read-only
net transport.Net // read-only
mutex sync.RWMutex // thread-safe
mutexTrMap sync.Mutex // thread-safe
log logging.LeveledLogger // read-only
@ -81,15 +83,18 @@ func NewClient(config *ClientConfig) (*Client, error) {
return nil, errNilConn
}
var err error
if config.Net == nil {
config.Net = vnet.NewNet(nil) // defaults to native operation
} else if config.Net.IsVirtual() {
log.Warn("vnet is enabled")
config.Net, err = stdnet.NewNet() // defaults to native operation
if err != nil {
return nil, err
}
} else if _, ok := config.Net.(*vnet.Net); ok {
log.Warn("Virtual network is enabled")
}
var stunServ, turnServ net.Addr
var stunServStr, turnServStr string
var err error
if len(config.STUNServerAddr) > 0 {
log.Debugf("resolving %s", config.STUNServerAddr)
stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr)
@ -333,9 +338,16 @@ func (c *Client) Allocate() (net.PacketConn, error) {
return relayedConn, nil
}
// CreatePermission Issues a CreatePermission request for the supplied addresses
// as described in https://datatracker.ietf.org/doc/html/rfc5766#section-9
func (c *Client) CreatePermission(addrs ...net.Addr) error {
return c.relayedUDPConn().CreatePermissions(addrs...)
}
// PerformTransaction performs STUN transaction
func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult bool) (client.TransactionResult,
error) {
error,
) {
trKey := b64.StdEncoding.EncodeToString(msg.TransactionID[:])
raw := make([]byte, len(msg.Raw))
@ -371,16 +383,16 @@ func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult
return res, nil
}
// OnDeallocated is called when deallocation of relay address has been complete.
// OnDeallocated is called when de-allocation of relay address has been complete.
// (Called by UDPConn)
func (c *Client) OnDeallocated(relayedAddr net.Addr) {
c.setRelayedUDPConn(nil)
}
// HandleInbound handles data received.
// This method handles incoming packet demultiplex it by the source address
// This method handles incoming packet de-multiplex it by the source address
// and the types of the message.
// This return a booleen (handled or not) and if there was an error.
// This return a boolean (handled or not) and if there was an error.
// Caller should check if the packet was handled by this client or not.
// If not handled, it is assumed that the packet is application data.
// If an error is returned, the caller should discard the packet regardless.
@ -413,7 +425,7 @@ func (c *Client) HandleInbound(data []byte, from net.Addr) (bool, error) {
return true, errNonSTUNMessage
default:
// assume, this is an application data
c.log.Tracef("non-STUN/TURN packect, unhandled")
c.log.Tracef("non-STUN/TURN packet, unhandled")
}
return false, nil
@ -529,7 +541,7 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) {
}
if nRtx == maxRtxCount {
// all retransmisstions failed
// all retransmissions failed
c.trMap.Delete(trKey)
if !tr.WriteResult(client.TransactionResult{
Err: fmt.Errorf("%w %s", errAllRetransmissionsFailed, trKey),

View file

@ -1,11 +0,0 @@
module github.com/pion/turn/v2
go 1.13
require (
github.com/pion/logging v0.2.2
github.com/pion/randutil v0.1.0
github.com/pion/stun v0.3.5
github.com/pion/transport v0.10.1
github.com/stretchr/testify v1.6.1
)

View file

@ -1,28 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -4,6 +4,7 @@ package allocation
import (
"net"
"sync"
"sync/atomic"
"time"
"github.com/pion/logging"
@ -12,6 +13,11 @@ import (
"github.com/pion/turn/v2/internal/proto"
)
type allocationResponse struct {
transactionID [stun.TransactionIDSize]byte
responseAttrs []stun.Setter
}
// Allocation is tied to a FiveTuple and relays traffic
// use CreateAllocation and GetAllocation to operate
type Allocation struct {
@ -27,6 +33,12 @@ type Allocation struct {
lifetimeTimer *time.Timer
closed chan interface{}
log logging.LeveledLogger
// some clients (Firefox or others using resiprocate's nICE lib) may retry allocation
// with same 5 tuple when received 413, for compatible with these clients,
// cache for response lost and client retry to implement 'stateless stack approach'
// https://datatracker.ietf.org/doc/html/rfc5766#section-6.2
responseCache atomic.Value // *allocationResponse
}
func addr2IPFingerprint(addr net.Addr) string {
@ -36,7 +48,7 @@ func addr2IPFingerprint(addr net.Addr) string {
case *net.TCPAddr: // Do we really need this case?
return a.IP.String()
}
return "" // shoud never happen
return "" // should never happen
}
// NewAllocation creates a new instance of NewAllocation.
@ -164,6 +176,22 @@ func (a *Allocation) Refresh(lifetime time.Duration) {
}
}
// SetResponseCache cache allocation response for retransmit allocation request
func (a *Allocation) SetResponseCache(transactionID [stun.TransactionIDSize]byte, attrs []stun.Setter) {
a.responseCache.Store(&allocationResponse{
transactionID: transactionID,
responseAttrs: attrs,
})
}
// GetResponseCache return response cache for retransmit allocation request
func (a *Allocation) GetResponseCache() (id [stun.TransactionIDSize]byte, attrs []stun.Setter) {
if res, ok := a.responseCache.Load().(*allocationResponse); ok && res != nil {
id, attrs = res.transactionID, res.responseAttrs
}
return
}
// Close closes the allocation
func (a *Allocation) Close() error {
select {
@ -210,7 +238,7 @@ func (a *Allocation) Close() error {
// transport address of the received UDP datagram. The Data indication
// is then sent on the 5-tuple associated with the allocation.
const rtpMTU = 1500
const rtpMTU = 1600
func (a *Allocation) packetHandler(m *Manager) {
buffer := make([]byte, rtpMTU)
@ -238,13 +266,19 @@ func (a *Allocation) packetHandler(m *Manager) {
a.log.Errorf("Failed to send ChannelData from allocation %v %v", srcAddr, err)
}
} else if p := a.GetPermission(srcAddr); p != nil {
udpAddr := srcAddr.(*net.UDPAddr)
udpAddr, ok := srcAddr.(*net.UDPAddr)
if !ok {
a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err)
return
}
peerAddressAttr := proto.PeerAddress{IP: udpAddr.IP, Port: udpAddr.Port}
dataAttr := proto.Data(buffer[:n])
msg, err := stun.Build(stun.TransactionID, stun.NewType(stun.MethodData, stun.ClassIndication), peerAddressAttr, dataAttr)
if err != nil {
a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err)
return
}
a.log.Debugf("relaying message from %s to client at %s",
srcAddr.String(),

View file

@ -14,6 +14,7 @@ type ManagerConfig struct {
LeveledLogger logging.LeveledLogger
AllocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error)
AllocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error)
PermissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool
}
type reservation struct {
@ -31,6 +32,7 @@ type Manager struct {
allocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error)
allocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error)
permissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool
}
// NewManager creates a new instance of Manager.
@ -49,6 +51,7 @@ func NewManager(config ManagerConfig) (*Manager, error) {
allocations: make(map[string]*Allocation, 64),
allocatePacketConn: config.AllocatePacketConn,
allocateConn: config.AllocateConn,
permissionHandler: config.PermissionHandler,
}, nil
}
@ -59,6 +62,13 @@ func (m *Manager) GetAllocation(fiveTuple *FiveTuple) *Allocation {
return m.allocations[fiveTuple.Fingerprint()]
}
// AllocationCount returns the number of existing allocations
func (m *Manager) AllocationCount() int {
m.lock.RLock()
defer m.lock.RUnlock()
return len(m.allocations)
}
// Close closes the manager and closes all allocations it manages
func (m *Manager) Close() error {
m.lock.Lock()
@ -168,19 +178,38 @@ func (m *Manager) GetReservation(reservationToken string) (int, bool) {
// GetRandomEvenPort returns a random un-allocated udp4 port
func (m *Manager) GetRandomEvenPort() (int, error) {
conn, addr, err := m.allocatePacketConn("udp4", 0)
if err != nil {
return 0, err
}
for i := 0; i < 128; i++ {
conn, addr, err := m.allocatePacketConn("udp4", 0)
if err != nil {
return 0, err
}
udpAddr, ok := addr.(*net.UDPAddr)
err = conn.Close()
if err != nil {
return 0, err
}
udpAddr, ok := addr.(*net.UDPAddr)
if !ok {
return 0, errFailedToCastUDPAddr
} else if err := conn.Close(); err != nil {
return 0, err
} else if udpAddr.Port%2 == 1 {
return m.GetRandomEvenPort()
if !ok {
return 0, errFailedToCastUDPAddr
}
if udpAddr.Port%2 == 0 {
return udpAddr.Port, nil
}
}
return udpAddr.Port, nil
return 0, errFailedToAllocateEvenPort
}
// GrantPermission handles permission requests by calling the permission handler callback
// associated with the TURN server listener socket
func (m *Manager) GrantPermission(sourceAddr net.Addr, peerIP net.IP) error {
// no permission handler: open
if m.permissionHandler == nil {
return nil
}
if m.permissionHandler(sourceAddr, peerIP) {
return nil
}
return errAdminProhibited
}

View file

@ -14,4 +14,6 @@ var (
errLifetimeZero = errors.New("allocations must not be created with a lifetime of 0")
errDupeFiveTuple = errors.New("allocation attempt created with duplicate FiveTuple")
errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to *net.UDPAddr")
errFailedToAllocateEvenPort = errors.New("failed to allocate an even port")
errAdminProhibited = errors.New("permission request administratively prohibited")
)

View file

@ -7,9 +7,10 @@ import (
"time"
)
// Chanel number:
// 0x4000 through 0x7FFF: These values are the allowed channel
// numbers (16,383 possible values).
// Channel number:
//
// 0x4000 through 0x7FFF: These values are the allowed channel
// numbers (16,383 possible values).
const (
minChannelNumber uint16 = 0x4000
maxChannelNumber uint16 = 0x7fff

View file

@ -153,6 +153,21 @@ func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
}
}
func (c *UDPConn) createPermission(perm *permission, addr net.Addr) error {
perm.mutex.Lock()
defer perm.mutex.Unlock()
if perm.state() == permStateIdle {
// punch a hole! (this would block a bit..)
if err := c.CreatePermissions(addr); err != nil {
c.permMap.delete(addr)
return err
}
perm.setState(permStatePermitted)
}
return nil
}
// WriteTo writes a packet with payload p to addr.
// WriteTo can be made to time out and return
// an Error with Timeout() == true after a fixed time limit;
@ -172,30 +187,15 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco
c.permMap.insert(addr, perm)
}
// This func-block would block, per destination IP (, or perm), until
// the perm state becomes "requested". Purpose of this is to guarantee
// the order of packets (within the same perm).
// Note that CreatePermission transaction may not be complete before
// all the data transmission. This is done assuming that the request
// will be mostly likely successful and we can tolerate some loss of
// UDP packet (or reorder), inorder to minimize the latency in most cases.
createPermission := func() error {
perm.mutex.Lock()
defer perm.mutex.Unlock()
if perm.state() == permStateIdle {
// punch a hole! (this would block a bit..)
if err = c.createPermissions(addr); err != nil {
c.permMap.delete(addr)
return err
}
perm.setState(permStatePermitted)
}
return nil
}
for i := 0; i < maxRetryAttempts; i++ {
if err = createPermission(); !errors.Is(err, errTryAgain) {
// c.createPermission() would block, per destination IP (, or perm),
// until the perm state becomes "requested". Purpose of this is to
// guarantee the order of packets (within the same perm).
// Note that CreatePermission transaction may not be complete before
// all the data transmission. This is done assuming that the request
// will be most likely successful and we can tolerate some loss of
// UDP packet (or reorder), inorder to minimize the latency in most cases.
if err = c.createPermission(perm, addr); !errors.Is(err, errTryAgain) {
break
}
}
@ -277,7 +277,11 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco
}()
// send via ChannelData
return c.sendChannelData(p, b.number)
_, err = c.sendChannelData(p, b.number)
if err != nil {
return 0, err
}
return len(p), nil
}
// Close closes the connection.
@ -359,7 +363,9 @@ func addr2PeerAddress(addr net.Addr) proto.PeerAddress {
return peerAddr
}
func (c *UDPConn) createPermissions(addrs ...net.Addr) error {
// CreatePermissions Issues a CreatePermission request for the supplied addresses
// as described in https://datatracker.ietf.org/doc/html/rfc5766#section-9
func (c *UDPConn) CreatePermissions(addrs ...net.Addr) error {
setters := []stun.Setter{
stun.TransactionID,
stun.NewType(stun.MethodCreatePermission, stun.ClassRequest),
@ -496,7 +502,7 @@ func (c *UDPConn) refreshPermissions() error {
c.log.Debug("no permission to refresh")
return nil
}
if err := c.createPermissions(addrs...); err != nil {
if err := c.CreatePermissions(addrs...); err != nil {
if errors.Is(err, errTryAgain) {
return errTryAgain
}
@ -549,7 +555,11 @@ func (c *UDPConn) sendChannelData(data []byte, chNum uint16) (int, error) {
Number: proto.ChannelNumber(chNum),
}
chData.Encode()
return c.obs.WriteTo(chData.Raw, c.obs.TURNServerAddr())
_, err := c.obs.WriteTo(chData.Raw, c.obs.TURNServerAddr())
if err != nil {
return 0, err
}
return len(data), nil
}
func (c *UDPConn) onRefreshTimers(id int) {

View file

@ -5,7 +5,7 @@ import (
)
var (
errFakeErr = errors.New("fake error")
errFake = errors.New("fake error")
errTryAgain = errors.New("try again")
errClosed = errors.New("use of closed network connection")
errUDPAddrCast = errors.New("addr is not a net.UDPAddr")

View file

@ -139,7 +139,7 @@ func NewTransactionMap() *TransactionMap {
}
}
// Insert inserts a trasaction to the map
// Insert inserts a transaction to the map
func (m *TransactionMap) Insert(key string, tr *Transaction) bool {
m.mutex.Lock()
defer m.mutex.Unlock()

View file

@ -10,7 +10,7 @@ type TryLock struct {
}
// Lock tries to lock the try-lock. If successful, it returns true.
// Otherwise, it returns false immedidately.
// Otherwise, it returns false immediately.
func (c *TryLock) Lock() error {
if !atomic.CompareAndSwapInt32(&c.n, 0, 1) {
return errDoubleLock

View file

@ -24,7 +24,7 @@ func AddrIPPort(a net.Addr) (net.IP, int, error) {
}
// AddrEqual asserts that two net.Addrs are equal
// Currently only supprots UDP but will be extended in the future to support others
// Currently only supports UDP but will be extended in the future to support others
func AddrEqual(a, b net.Addr) bool {
aUDP, ok := a.(*net.UDPAddr)
if !ok {

View file

@ -11,7 +11,7 @@ import (
//
// See RFC 5766 Section 11.4
type ChannelData struct {
Data []byte // can be subslice of Raw
Data []byte // can be sub slice of Raw
Length int // ignored while encoding, len(Data) is used
Number ChannelNumber
Raw []byte

View file

@ -0,0 +1,39 @@
package proto
import (
"encoding/binary"
"github.com/pion/stun"
)
// ConnectionID represents CONNECTION-ID attribute.
//
// The CONNECTION-ID attribute uniquely identifies a peer data
// connection. It is a 32-bit unsigned integral value.
//
// RFC 6062 Section 6.2.1
type ConnectionID uint32
const connectionIDSize = 4 // uint32: 4 bytes, 32 bits
// AddTo adds CONNECTION-ID to message.
func (c ConnectionID) AddTo(m *stun.Message) error {
v := make([]byte, lifetimeSize)
binary.BigEndian.PutUint32(v, uint32(c))
m.Add(stun.AttrConnectionID, v)
return nil
}
// GetFrom decodes CONNECTION-ID from message.
func (c *ConnectionID) GetFrom(m *stun.Message) error {
v, err := m.Get(stun.AttrConnectionID)
if err != nil {
return err
}
if err = stun.CheckSize(stun.AttrConnectionID, len(v), connectionIDSize); err != nil {
return err
}
_ = v[connectionIDSize-1] // asserting length
*(*uint32)(c) = binary.BigEndian.Uint32(v)
return nil
}

View file

@ -1,18 +1,45 @@
package proto
import "github.com/pion/stun"
import (
"github.com/pion/stun"
)
// DontFragmentAttr represents DONT-FRAGMENT attribute.
type DontFragmentAttr struct{}
// DontFragmentAttr is a deprecated alias for DontFragment
// Deprecated: Please use DontFragment
type DontFragmentAttr = DontFragment
// DontFragment represents DONT-FRAGMENT attribute.
//
// This attribute is used by the client to request that the server set
// the DF (Don't Fragment) bit in the IP header when relaying the
// application data onward to the peer. This attribute has no value
// part and thus the attribute length field is 0.
//
// RFC 5766 Section 14.8
type DontFragment struct{}
const dontFragmentSize = 0
// AddTo adds DONT-FRAGMENT attribute to message.
func (DontFragmentAttr) AddTo(m *stun.Message) error {
func (DontFragment) AddTo(m *stun.Message) error {
m.Add(stun.AttrDontFragment, nil)
return nil
}
// GetFrom decodes DONT-FRAGMENT from message.
func (d *DontFragment) GetFrom(m *stun.Message) error {
v, err := m.Get(stun.AttrDontFragment)
if err != nil {
return err
}
if err = stun.CheckSize(stun.AttrDontFragment, len(v), dontFragmentSize); err != nil {
return err
}
return nil
}
// IsSet returns true if DONT-FRAGMENT attribute is set.
func (DontFragmentAttr) IsSet(m *stun.Message) bool {
func (DontFragment) IsSet(m *stun.Message) bool {
_, err := m.Get(stun.AttrDontFragment)
return err == nil
}

View file

@ -1,111 +0,0 @@
// +build gofuzz
package proto
import (
"fmt"
"github.com/pion/stun"
)
type attr interface {
stun.Getter
stun.Setter
}
type attrs []struct {
g attr
t stun.AttrType
}
func (a attrs) pick(v byte) struct {
g attr
t stun.AttrType
} {
idx := int(v) % len(a)
return a[idx]
}
func FuzzSetters(data []byte) int {
var (
m1 = &stun.Message{
Raw: make([]byte, 0, 2048),
}
m2 = &stun.Message{
Raw: make([]byte, 0, 2048),
}
m3 = &stun.Message{
Raw: make([]byte, 0, 2048),
}
)
attributes := attrs{
{new(RequestedTransport), stun.AttrRequestedTransport},
{new(RelayedAddress), stun.AttrXORRelayedAddress},
{new(ChannelNumber), stun.AttrChannelNumber},
{new(Data), stun.AttrData},
{new(EvenPort), stun.AttrEvenPort},
{new(Lifetime), stun.AttrLifetime},
{new(ReservationToken), stun.AttrReservationToken},
}
var firstByte = byte(0)
if len(data) > 0 {
firstByte = data[0]
}
a := attributes.pick(firstByte)
value := data
if len(data) > 1 {
value = value[1:]
}
m1.WriteHeader()
m1.Add(a.t, value)
err := a.g.GetFrom(m1)
if err == stun.ErrAttributeNotFound {
fmt.Println("unexpected 404") // nolint
panic(err) // nolint
}
if err != nil {
return 1
}
m2.WriteHeader()
if err := a.g.AddTo(m2); err != nil {
fmt.Println("failed to add attribute to m2") // nolint
panic(err) // nolint
}
m3.WriteHeader()
v, err := m2.Get(a.t)
if err != nil {
panic(err) // nolint
}
m3.Add(a.t, v)
if !m2.Equal(m3) {
fmt.Println(m2, "not equal", m3) // nolint
panic("not equal") // nolint
}
return 1
}
var d = &ChannelData{}
func FuzzChannelData(data []byte) int {
d.Reset()
if b := bin.Uint16(data[0:4]); b > 20000 {
bin.PutUint16(data[0:4], MinChannelNumber-1)
} else if b > 40000 {
bin.PutUint16(data[0:4], MinChannelNumber+(MaxChannelNumber-MinChannelNumber)%b)
}
d.Raw = append(d.Raw, data...)
if d.Decode() != nil {
return 0
}
d2 := &ChannelData{}
d.Encode()
if !d.Number.Valid() {
return 1
}
d2.Raw = d.Raw
if err := d2.Decode(); err != nil {
panic(err) //nolint
}
return 1
}

View file

@ -27,7 +27,7 @@ func (p Protocol) String() string {
//
// This attribute is used by the client to request a specific transport
// protocol for the allocated transport address. RFC 5766 only allows the use of
// codepoint 17 (User Datagram Protocol).
// code point 17 (User Datagram Protocol).
//
// RFC 5766 Section 14.7
type RequestedTransport struct {

View file

@ -33,14 +33,20 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
reservationToken := ""
badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
insufficentCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity})
insufficientCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity})
// 2. The server checks if the 5-tuple is currently in use by an
// existing allocation. If yes, the server rejects the request with
// a 437 (Allocation Mismatch) error.
if alloc := r.AllocationManager.GetAllocation(fiveTuple); alloc != nil {
msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch})
return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...)
id, attrs := alloc.GetResponseCache()
if id != m.TransactionID {
msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch})
return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...)
}
// a retry allocation
msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(attrs, messageIntegrity)...)
return buildAndSend(r.Conn, r.SrcAddr, msg...)
}
// 3. The server checks if the request contains a REQUESTED-TRANSPORT
@ -91,10 +97,10 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
// error.
var evenPort proto.EvenPort
if err = evenPort.GetFrom(m); err == nil {
randomPort := 0
var randomPort int
randomPort, err = r.AllocationManager.GetRandomEvenPort()
if err != nil {
return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...)
return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...)
}
requestedPort = randomPort
reservationToken = randSeq(8)
@ -118,7 +124,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
requestedPort,
lifetimeDuration)
if err != nil {
return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...)
return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...)
}
// Once the allocation is created, the server replies with a success
@ -162,6 +168,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
}
msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(responseAttrs, messageIntegrity)...)
a.SetResponseCache(m.TransactionID, responseAttrs)
return buildAndSend(r.Conn, r.SrcAddr, msg...)
}
@ -224,8 +231,15 @@ func handleCreatePermissionRequest(r Request, m *stun.Message) error {
return err
}
if err := r.AllocationManager.GrantPermission(r.SrcAddr, peerAddress.IP); err != nil {
r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr.String(),
peerAddress.IP.String())
return err
}
r.Log.Debugf("adding permission for %s", fmt.Sprintf("%s:%d",
peerAddress.IP.String(), peerAddress.Port))
a.AddPermission(allocation.NewPermission(
&net.UDPAddr{
IP: peerAddress.IP,
@ -309,6 +323,16 @@ func handleChannelBindRequest(r Request, m *stun.Message) error {
return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
}
if err = r.AllocationManager.GrantPermission(r.SrcAddr, peerAddr.IP); err != nil {
r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr.String(),
peerAddr.IP.String())
unauthorizedRequestMsg := buildMsg(m.TransactionID,
stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse),
&stun.ErrorCodeAttribute{Code: stun.CodeUnauthorized})
return buildAndSendErr(r.Conn, r.SrcAddr, err, unauthorizedRequestMsg...)
}
r.Log.Debugf("binding channel %d to %s",
channel,
fmt.Sprintf("%s:%d", peerAddr.IP.String(), peerAddr.Port))

View file

@ -95,8 +95,13 @@ func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method)
}
// Assert Nonce exists and is not expired
nonceCreationTime, ok := r.Nonces.Load(string(*nonceAttr))
if !ok || time.Since(nonceCreationTime.(time.Time)) >= nonceLifetime {
nonceCreationTime, nonceFound := r.Nonces.Load(string(*nonceAttr))
if !nonceFound {
r.Nonces.Delete(nonceAttr)
return respondWithNonce(stun.CodeStaleNonce)
}
if timeValue, ok := nonceCreationTime.(time.Time); !ok || time.Since(timeValue) >= nonceLifetime {
r.Nonces.Delete(nonceAttr)
return respondWithNonce(stun.CodeStaleNonce)
}

View file

@ -11,7 +11,7 @@ import ( //nolint:gci
"github.com/pion/logging"
)
// GenerateCredentials can be used to create credentials valid for [duration] time
// GenerateLongTermCredentials can be used to create credentials valid for [duration] time
func GenerateLongTermCredentials(sharedSecret string, duration time.Duration) (string, string, error) {
t := time.Now().Add(duration).Unix()
username := strconv.FormatInt(t, 10)
@ -29,14 +29,14 @@ func longTermCredentials(username string, sharedSecret string) (string, error) {
return base64.StdEncoding.EncodeToString(password), nil
}
// NewAuthHandler returns a turn.AuthAuthHandler used with Long Term (or Time Windowed) Credentials.
// NewLongTermAuthHandler returns a turn.AuthAuthHandler used with Long Term (or Time Windowed) Credentials.
// https://tools.ietf.org/search/rfc5389#section-10.2
func NewLongTermAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHandler {
if l == nil {
l = logging.NewDefaultLoggerFactory().NewLogger("turn")
}
return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) {
l.Tracef("Authentication username=%q realm=%q srcAddr=%v\n", username, realm, srcAddr)
l.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr)
t, err := strconv.Atoi(username)
if err != nil {
l.Errorf("Invalid time-windowed username %q", username)

View file

@ -1,10 +1,12 @@
package turn
import (
"fmt"
"net"
"strconv"
"github.com/pion/transport/vnet"
"github.com/pion/transport/v2"
"github.com/pion/transport/v2/stdnet"
)
// RelayAddressGeneratorNone returns the listener with no modifications
@ -12,13 +14,17 @@ type RelayAddressGeneratorNone struct {
// Address is passed to Listen/ListenPacket when creating the Relay
Address string
Net *vnet.Net
Net transport.Net
}
// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured
// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured
func (r *RelayAddressGeneratorNone) Validate() error {
if r.Net == nil {
r.Net = vnet.NewNet(nil)
var err error
r.Net, err = stdnet.NewNet()
if err != nil {
return fmt.Errorf("failed to create network: %w", err)
}
}
switch {

View file

@ -5,7 +5,8 @@ import (
"net"
"github.com/pion/randutil"
"github.com/pion/transport/vnet"
"github.com/pion/transport/v2"
"github.com/pion/transport/v2/stdnet"
)
// RelayAddressGeneratorPortRange can be used to only allocate connections inside a defined port range.
@ -28,13 +29,17 @@ type RelayAddressGeneratorPortRange struct {
// Address is passed to Listen/ListenPacket when creating the Relay
Address string
Net *vnet.Net
Net transport.Net
}
// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured
func (r *RelayAddressGeneratorPortRange) Validate() error {
if r.Net == nil {
r.Net = vnet.NewNet(nil)
var err error
r.Net, err = stdnet.NewNet()
if err != nil {
return fmt.Errorf("failed to create network: %w", err)
}
}
if r.Rand == nil {
@ -66,7 +71,11 @@ func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requ
if err != nil {
return nil, nil, err
}
relayAddr := conn.LocalAddr().(*net.UDPAddr)
relayAddr, ok := conn.LocalAddr().(*net.UDPAddr)
if !ok {
return nil, nil, errNilConn
}
relayAddr.IP = r.RelayAddress
return conn, relayAddr, nil
}
@ -78,7 +87,11 @@ func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requ
continue
}
relayAddr := conn.LocalAddr().(*net.UDPAddr)
relayAddr, ok := conn.LocalAddr().(*net.UDPAddr)
if !ok {
return nil, nil, errNilConn
}
relayAddr.IP = r.RelayAddress
return conn, relayAddr, nil
}

View file

@ -1,10 +1,12 @@
package turn
import (
"fmt"
"net"
"strconv"
"github.com/pion/transport/vnet"
"github.com/pion/transport/v2"
"github.com/pion/transport/v2/stdnet"
)
// RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created.
@ -16,13 +18,17 @@ type RelayAddressGeneratorStatic struct {
// Address is passed to Listen/ListenPacket when creating the Relay
Address string
Net *vnet.Net
Net transport.Net
}
// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured
// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured
func (r *RelayAddressGeneratorStatic) Validate() error {
if r.Net == nil {
r.Net = vnet.NewNet(nil)
var err error
r.Net, err = stdnet.NewNet()
if err != nil {
return fmt.Errorf("failed to create network: %w", err)
}
}
switch {
@ -43,7 +49,11 @@ func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, request
}
// Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic
relayAddr := conn.LocalAddr().(*net.UDPAddr)
relayAddr, ok := conn.LocalAddr().(*net.UDPAddr)
if !ok {
return nil, nil, errNilConn
}
relayAddr.IP = r.RelayAddress
return conn, relayAddr, nil

View file

@ -1,15 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"postUpdateOptions": [
"gomodTidy"
],
"commitBody": "Generated by renovateBot",
"packageRules": [
{
"packagePatterns": ["^golang.org/x/"],
"schedule": ["on the first day of the month"]
}
"github>pion/renovate-config"
]
}

View file

@ -14,7 +14,7 @@ import (
)
const (
inboundMTU = 1500
defaultInboundMTU = 1600
)
// Server is an instance of the Pion TURN Server
@ -25,11 +25,15 @@ type Server struct {
channelBindTimeout time.Duration
nonces *sync.Map
packetConnConfigs []PacketConnConfig
listenerConfigs []ListenerConfig
packetConnConfigs []PacketConnConfig
listenerConfigs []ListenerConfig
allocationManagers []*allocation.Manager
inboundMTU int
}
// NewServer creates the Pion TURN server
//
//nolint:gocognit
func NewServer(config ServerConfig) (*Server, error) {
if err := config.validate(); err != nil {
return nil, err
@ -40,6 +44,11 @@ func NewServer(config ServerConfig) (*Server, error) {
loggerFactory = logging.NewDefaultLoggerFactory()
}
mtu := defaultInboundMTU
if config.InboundMTU != 0 {
mtu = config.InboundMTU
}
s := &Server{
log: loggerFactory.NewLogger("turn"),
authHandler: config.AuthHandler,
@ -48,77 +57,55 @@ func NewServer(config ServerConfig) (*Server, error) {
packetConnConfigs: config.PacketConnConfigs,
listenerConfigs: config.ListenerConfigs,
nonces: &sync.Map{},
inboundMTU: mtu,
}
if s.channelBindTimeout == 0 {
s.channelBindTimeout = proto.DefaultLifetime
}
for i := range s.packetConnConfigs {
go func(p PacketConnConfig) {
allocationManager, err := allocation.NewManager(allocation.ManagerConfig{
AllocatePacketConn: p.RelayAddressGenerator.AllocatePacketConn,
AllocateConn: p.RelayAddressGenerator.AllocateConn,
LeveledLogger: s.log,
})
if err != nil {
s.log.Errorf("exit read loop on error: %s", err.Error())
return
}
defer func() {
if err := allocationManager.Close(); err != nil {
s.log.Errorf("Failed to close AllocationManager: %s", err.Error())
}
}()
for _, cfg := range s.packetConnConfigs {
am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler)
if err != nil {
return nil, fmt.Errorf("failed to create AllocationManager: %w", err)
}
s.readLoop(p.PacketConn, allocationManager)
}(s.packetConnConfigs[i])
go s.readPacketConn(cfg, am)
}
for _, listener := range s.listenerConfigs {
go func(l ListenerConfig) {
allocationManager, err := allocation.NewManager(allocation.ManagerConfig{
AllocatePacketConn: l.RelayAddressGenerator.AllocatePacketConn,
AllocateConn: l.RelayAddressGenerator.AllocateConn,
LeveledLogger: s.log,
})
if err != nil {
s.log.Errorf("exit read loop on error: %s", err.Error())
return
}
defer func() {
if err := allocationManager.Close(); err != nil {
s.log.Errorf("Failed to close AllocationManager: %s", err.Error())
}
}()
for _, cfg := range s.listenerConfigs {
am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler)
if err != nil {
return nil, fmt.Errorf("failed to create AllocationManager: %w", err)
}
for {
conn, err := l.Listener.Accept()
if err != nil {
s.log.Debugf("exit accept loop on error: %s", err.Error())
return
}
go s.readLoop(NewSTUNConn(conn), allocationManager)
}
}(listener)
go s.readListener(cfg, am)
}
return s, nil
}
// AllocationCount returns the number of active allocations. It can be used to drain the server before closing
func (s *Server) AllocationCount() int {
allocs := 0
for _, am := range s.allocationManagers {
allocs += am.AllocationCount()
}
return allocs
}
// Close stops the TURN Server. It cleans up any associated state and closes all connections it is managing
func (s *Server) Close() error {
var errors []error
for _, p := range s.packetConnConfigs {
if err := p.PacketConn.Close(); err != nil {
for _, cfg := range s.packetConnConfigs {
if err := cfg.PacketConn.Close(); err != nil {
errors = append(errors, err)
}
}
for _, l := range s.listenerConfigs {
if err := l.Listener.Close(); err != nil {
for _, cfg := range s.listenerConfigs {
if err := cfg.Listener.Close(); err != nil {
errors = append(errors, err)
}
}
@ -129,19 +116,68 @@ func (s *Server) Close() error {
err := errFailedToClose
for _, e := range errors {
err = fmt.Errorf("%s; Close error (%v) ", err.Error(), e) //nolint:goerr113
err = fmt.Errorf("%s; close error (%w) ", err, e)
}
return err
}
func (s *Server) readPacketConn(p PacketConnConfig, am *allocation.Manager) {
s.readLoop(p.PacketConn, am)
if err := am.Close(); err != nil {
s.log.Errorf("Failed to close AllocationManager: %s", err)
}
}
func (s *Server) readListener(l ListenerConfig, am *allocation.Manager) {
defer func() {
if err := am.Close(); err != nil {
s.log.Errorf("Failed to close AllocationManager: %s", err)
}
}()
for {
conn, err := l.Listener.Accept()
if err != nil {
s.log.Debugf("Failed to accept: %s", err)
return
}
go s.readLoop(NewSTUNConn(conn), am)
}
}
func (s *Server) createAllocationManager(addrGenerator RelayAddressGenerator, handler PermissionHandler) (*allocation.Manager, error) {
if handler == nil {
handler = DefaultPermissionHandler
}
am, err := allocation.NewManager(allocation.ManagerConfig{
AllocatePacketConn: addrGenerator.AllocatePacketConn,
AllocateConn: addrGenerator.AllocateConn,
PermissionHandler: handler,
LeveledLogger: s.log,
})
if err != nil {
return am, err
}
s.allocationManagers = append(s.allocationManagers, am)
return am, err
}
func (s *Server) readLoop(p net.PacketConn, allocationManager *allocation.Manager) {
buf := make([]byte, inboundMTU)
buf := make([]byte, s.inboundMTU)
for {
n, addr, err := p.ReadFrom(buf)
if err != nil {
switch {
case err != nil:
s.log.Debugf("exit read loop on error: %s", err.Error())
return
case n >= s.inboundMTU:
s.log.Debugf("Read bytes exceeded MTU, packet is possibly truncated")
}
if err := server.HandleRequest(server.Request{

View file

@ -23,6 +23,19 @@ type RelayAddressGenerator interface {
AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error)
}
// PermissionHandler is a callback to filter incoming CreatePermission and ChannelBindRequest
// requests based on the client IP address and port and the peer IP address the client intends to
// connect to. If the client is behind a NAT then the filter acts on the server reflexive
// ("mapped") address instead of the real client IP address and port. Note that TURN permissions
// are per-allocation and per-peer-IP-address, to mimic the address-restricted filtering mechanism
// of NATs that comply with [RFC4787], see https://tools.ietf.org/html/rfc5766#section-2.3.
type PermissionHandler func(clientAddr net.Addr, peerIP net.IP) (ok bool)
// DefaultPermissionHandler is convince function that grants permission to all peers
func DefaultPermissionHandler(clientAddr net.Addr, peerIP net.IP) (ok bool) {
return true
}
// PacketConnConfig is a single net.PacketConn to listen/write on. This will be used for UDP listeners
type PacketConnConfig struct {
PacketConn net.PacketConn
@ -30,6 +43,11 @@ type PacketConnConfig struct {
// When an allocation is generated the RelayAddressGenerator
// creates the net.PacketConn and returns the IP/Port it is available at
RelayAddressGenerator RelayAddressGenerator
// PermissionHandler is a callback to filter peer addresses. Can be set as nil, in which
// case the DefaultPermissionHandler is automatically instantiated to admit all peer
// connections
PermissionHandler PermissionHandler
}
func (c *PacketConnConfig) validate() error {
@ -50,6 +68,11 @@ type ListenerConfig struct {
// When an allocation is generated the RelayAddressGenerator
// creates the net.PacketConn and returns the IP/Port it is available at
RelayAddressGenerator RelayAddressGenerator
// PermissionHandler is a callback to filter peer addresses. Can be set as nil, in which
// case the DefaultPermissionHandler is automatically instantiated to admit all peer
// connections
PermissionHandler PermissionHandler
}
func (c *ListenerConfig) validate() error {
@ -67,7 +90,7 @@ func (c *ListenerConfig) validate() error {
// AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior
type AuthHandler func(username, realm string, srcAddr net.Addr) (key []byte, ok bool)
// GenerateAuthKey is a convince function to easily generate keys in the format used by AuthHandler
// GenerateAuthKey is a convenience function to easily generate keys in the format used by AuthHandler
func GenerateAuthKey(username, realm, password string) []byte {
// #nosec
h := md5.New()
@ -93,6 +116,9 @@ type ServerConfig struct {
// ChannelBindTimeout sets the lifetime of channel binding. Defaults to 10 minutes.
ChannelBindTimeout time.Duration
// Sets the server inbound MTU(Maximum transmition unit). Defaults to 1600 bytes.
InboundMTU int
}
func (s *ServerConfig) validate() error {

View file

@ -42,16 +42,19 @@ func consumeSingleTURNFrame(p []byte) (int, error) {
}
var datagramSize uint16
if stun.IsMessage(p) {
switch {
case stun.IsMessage(p):
datagramSize = binary.BigEndian.Uint16(p[2:4]) + stunHeaderSize
} else if num := binary.BigEndian.Uint16(p[0:4]); proto.ChannelNumber(num).Valid() {
case proto.ChannelNumber(binary.BigEndian.Uint16(p[0:2])).Valid():
datagramSize = binary.BigEndian.Uint16(p[channelDataNumberSize:channelDataHeaderSize])
if paddingOverflow := (datagramSize + channelDataPadding) % channelDataPadding; paddingOverflow != 0 {
datagramSize = (datagramSize + channelDataPadding) - paddingOverflow
}
datagramSize += channelDataHeaderSize
} else {
case len(p) < stunHeaderSize:
return 0, errIncompleteTURNFrame
default:
return 0, errInvalidTURNFrame
}