直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
28
server/vendor/github.com/pion/stun/.gitignore
generated
vendored
Normal file
28
server/vendor/github.com/pion/stun/.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/stun/.golangci.yml
generated
vendored
Normal file
137
server/vendor/github.com/pion/stun/.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/stun/.goreleaser.yml
generated
vendored
Normal file
5
server/vendor/github.com/pion/stun/.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
|
||||
40
server/vendor/github.com/pion/stun/AUTHORS.txt
generated
vendored
Normal file
40
server/vendor/github.com/pion/stun/AUTHORS.txt
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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
|
||||
Adam Kiss <masterada@gmail.com>
|
||||
Aleksandr Razumov <ar@cydev.ru>
|
||||
Aleksandr Razumov <ar@gortc.io>
|
||||
Atsushi Watanabe <atsushi.w@ieee.org>
|
||||
backkem <mail@backkem.me>
|
||||
Cecylia Bocovich <cohosh@torproject.org>
|
||||
Christian Muehlhaeuser <muesli@gmail.com>
|
||||
David-dp- <David-dp-@users.noreply.github.com>
|
||||
ernado <ar@cydev.ru>
|
||||
ernado <ernado@ya.ru>
|
||||
fossabot <badges@fossa.io>
|
||||
Frank Dietrich <bits_n_bytes@gmx.de>
|
||||
Hugo Arregui <hugo.arregui@gmail.com>
|
||||
Jerry Tao <taojay315@gmail.com>
|
||||
jinleileiking <jinleileiking@gmail.com>
|
||||
John Bradley <john@pion.ly>
|
||||
Juliusz Chroboczek <jch@irif.fr>
|
||||
Maanas Royy <m4manas@gmail.com>
|
||||
Moises Marangoni <moises.marangon@gmail.com>
|
||||
Raphael Randschau <nicolai86@me.com>
|
||||
Sean DuBois <seaduboi@amazon.com>
|
||||
Sean DuBois <sean@pion.ly>
|
||||
Sean DuBois <sean@siobud.com>
|
||||
songjiayang <songjiayang@users.noreply.github.com>
|
||||
Steffen Vogel <post@steffenvogel.de>
|
||||
Vladislav Yarmak <vladislav@vm-0.com>
|
||||
Will LE <lexuandinhct@gmail.com>
|
||||
Y.Horie <u5.horie@gmail.com>
|
||||
Yutaka Takeda <yt0916@gmail.com>
|
||||
ZHENK <chengzhenyang@gmail.com>
|
||||
|
||||
# List of contributors not appearing in Git history
|
||||
Aliaksandr Valialkin <valyala@gmail.com>
|
||||
The IETF Trust
|
||||
The gortc project
|
||||
9
server/vendor/github.com/pion/stun/LICENSE
generated
vendored
Normal file
9
server/vendor/github.com/pion/stun/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.
|
||||
39
server/vendor/github.com/pion/stun/Makefile
generated
vendored
Normal file
39
server/vendor/github.com/pion/stun/Makefile
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
VERSION := $(shell git describe --tags | sed -e 's/^v//g' | awk -F "-" '{print $$1}')
|
||||
ITERATION := $(shell git describe --tags --long | awk -F "-" '{print $$2}')
|
||||
GO_VERSION=$(shell gobuild -v)
|
||||
GO := $(or $(GOROOT),/usr/lib/go)/bin/go
|
||||
PROCS := $(shell nproc)
|
||||
cores:
|
||||
@echo "cores: $(PROCS)"
|
||||
bench:
|
||||
go test -bench .
|
||||
bench-record:
|
||||
$(GO) test -bench . > "benchmarks/stun-go-$(GO_VERSION).txt"
|
||||
lint:
|
||||
@golangci-lint run ./...
|
||||
@echo "ok"
|
||||
escape:
|
||||
@echo "Not escapes, except autogenerated:"
|
||||
@go build -gcflags '-m -l' 2>&1 \
|
||||
| grep -v "<autogenerated>" \
|
||||
| grep escapes
|
||||
format:
|
||||
goimports -w .
|
||||
bench-compare:
|
||||
go test -bench . > bench.go-16
|
||||
go-tip test -bench . > bench.go-tip
|
||||
@benchcmp bench.go-16 bench.go-tip
|
||||
install:
|
||||
go get gortc.io/api
|
||||
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
test-integration:
|
||||
@cd e2e && bash ./test.sh
|
||||
prepush: test lint test-integration
|
||||
check-api:
|
||||
@cd api && bash ./check.sh
|
||||
test:
|
||||
@./go.test.sh
|
||||
clean:
|
||||
186
server/vendor/github.com/pion/stun/README.md
generated
vendored
Normal file
186
server/vendor/github.com/pion/stun/README.md
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
Pion STUN
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of STUN</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-stun-gray.svg?longCache=true&colorB=brightgreen" alt="Pion stun"></a>
|
||||
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/stun/test.yaml">
|
||||
<a href="https://pkg.go.dev/github.com/pion/stun"><img src="https://pkg.go.dev/badge/github.com/pion/stun.svg" alt="Go Reference"></a>
|
||||
<a href="https://codecov.io/gh/pion/stun"><img src="https://codecov.io/gh/pion/stun/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/stun"><img src="https://goreportcard.com/badge/github.com/pion/stun" 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>
|
||||
|
||||
Package `stun` implements Session Traversal Utilities for NAT (STUN) ([RFC 5389][rfc5389])
|
||||
protocol and [client](https://pkg.go.dev/github.com/pion/stun#Client) with no external dependencies and zero allocations in hot paths.
|
||||
Client [supports](https://pkg.go.dev/github.com/pion/stun#WithRTO) automatic request retransmissions.
|
||||
|
||||
### Example
|
||||
You can get your current IP address from any STUN server by sending
|
||||
binding request. See more idiomatic example at `cmd/stun-client`.
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/stun"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse a STUN URI
|
||||
u, err := stun.ParseURI("stun:stun.l.google.com:19302")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Creating a "connection" to STUN server.
|
||||
c, err := stun.DialURI(u, &stun.DialConfig{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Building binding request with random transaction id.
|
||||
message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
|
||||
// Sending request to STUN server, waiting for response message.
|
||||
if err := c.Do(message, func(res stun.Event) {
|
||||
if res.Error != nil {
|
||||
panic(res.Error)
|
||||
}
|
||||
// Decoding XOR-MAPPED-ADDRESS attribute from message.
|
||||
var xorAddr stun.XORMappedAddress
|
||||
if err := xorAddr.GetFrom(res.Message); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("your IP is", xorAddr.IP)
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### RFCs
|
||||
#### Implemented
|
||||
- **RFC 5389**: [Session Traversal Utilities for NAT (STUN)][rfc5389]
|
||||
- **RFC 5769**: [Test Vectors for Session Traversal Utilities for NAT (STUN)][rfc5769]
|
||||
- **RFC 6062**: [Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations][rfc6062]
|
||||
- **RFC 7064**: [URI Scheme for the Session Traversal Utilities for NAT (STUN) Protocol][rfc7064]
|
||||
- **RFC 7065**: [Traversal Using Relays around NAT (TURN) Uniform Resource Identifiers][rfc7065]
|
||||
- **RFC 5780**: [NAT Behavior Discovery Using Session Traversal Utilities for NAT (STUN)][rfc5780] via [cmd/stun-nat-behaviour](cmd/stun-nat-behaviour)
|
||||
- (TLS-over-)TCP client support
|
||||
|
||||
#### Planned
|
||||
- **RFC 5389**: [ALTERNATE-SERVER](https://tools.ietf.org/html/rfc5389#section-11) support [#48](https://github.com/pion/stun/issues/48)
|
||||
|
||||
#### Compatability notes
|
||||
|
||||
[RFC 5389][rfc5389] obsoletes [RFC 3489][rfc3489], so implementation was ignored by purpose, however,
|
||||
[RFC 3489][rfc3489] can be easily implemented as separate package.
|
||||
|
||||
[rfc3489]: https://tools.ietf.org/html/rfc3489
|
||||
[rfc5389]: https://tools.ietf.org/html/rfc5389
|
||||
[rfc5769]: https://tools.ietf.org/html/rfc5769
|
||||
[rfc5780]: https://tools.ietf.org/html/rfc5780
|
||||
[rfc6062]: https://tools.ietf.org/html/rfc6062
|
||||
[rfc7064]: https://tools.ietf.org/html/rfc7064
|
||||
[rfc7065]: https://tools.ietf.org/html/rfc7065
|
||||
|
||||
### Stability
|
||||
Package is currently stable, no backward incompatible changes are expected
|
||||
with exception of critical bugs or security fixes.
|
||||
|
||||
Additional attributes are unlikely to be implemented in scope of stun package,
|
||||
the only exception is constants for attribute or message types.
|
||||
|
||||
### Requirements
|
||||
Go 1.12 is currently supported and tested in CI.
|
||||
|
||||
### Testing
|
||||
Client behavior is tested and verified in many ways:
|
||||
* End-To-End with long-term credentials
|
||||
* **coturn**: The coturn [server](https://github.com/coturn/coturn/wiki/turnserver) (linux)
|
||||
* Bunch of code static checkers (linters)
|
||||
* Standard unit-tests with coverage reporting (linux {amd64, **arm**64}, windows and darwin)
|
||||
* Explicit API backward compatibility [check](https://github.com/gortc/api), see `api` directory
|
||||
|
||||
See [TeamCity project](https://tc.gortc.io/project.html?projectId=stun&guest=1) and `e2e` directory
|
||||
for more information. Also the Wireshark `.pcap` files are available for e2e test in
|
||||
artifacts for build.
|
||||
|
||||
### Benchmarks
|
||||
Intel(R) Core(TM) i7-8700K:
|
||||
|
||||
```
|
||||
version: 1.22.2
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/pion/stun
|
||||
PASS
|
||||
benchmark iter time/iter throughput bytes alloc allocs
|
||||
--------- ---- --------- ---------- ----------- ------
|
||||
BenchmarkMappedAddress_AddTo-12 32489450 38.30 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAlternateServer_AddTo-12 31230991 39.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAgent_GC-12 431390 2918.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAgent_Process-12 35901940 36.20 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_GetNotFound-12 242004358 5.19 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_Get-12 230520343 5.21 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkClient_Do-12 1282231 943.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkErrorCode_AddTo-12 16318916 75.50 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkErrorCodeAttribute_AddTo-12 21584140 54.80 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkErrorCodeAttribute_GetFrom-12 100000000 11.10 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFingerprint_AddTo-12 19368768 64.00 ns/op 687.81 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkFingerprint_Check-12 24167007 49.10 ns/op 1057.99 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkBuildOverhead/Build-12 5486252 224.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkBuildOverhead/BuildNonPointer-12 2496544 517.00 ns/op 100 B/op 4 allocs/op
|
||||
BenchmarkBuildOverhead/Raw-12 6652118 181.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_ForEach-12 28254212 35.90 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessageIntegrity_AddTo-12 1000000 1179.00 ns/op 16.96 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessageIntegrity_Check-12 975954 1219.00 ns/op 26.24 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_Write-12 41040598 30.40 ns/op 922.13 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessageType_Value-12 1000000000 0.53 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_WriteTo-12 94942935 11.30 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_ReadFrom-12 43437718 29.30 ns/op 682.87 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_ReadBytes-12 74693397 15.90 ns/op 1257.42 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkIsMessage-12 1000000000 1.20 ns/op 16653.64 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_NewTransactionID-12 521121 2450.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessageFull-12 5389495 221.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessageFullHardcore-12 12715876 94.40 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_WriteHeader-12 100000000 11.60 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_CloneTo-12 30199020 41.80 ns/op 1626.66 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMessage_AddTo-12 415257625 2.97 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkDecode-12 49573747 23.60 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUsername_AddTo-12 56282674 22.50 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUsername_GetFrom-12 100000000 10.10 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkNonce_AddTo-12 39419097 35.80 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkNonce_AddTo_BadLength-12 196291666 6.04 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkNonce_GetFrom-12 120857732 9.93 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnknownAttributes/AddTo-12 28881430 37.20 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkUnknownAttributes/GetFrom-12 64907534 19.80 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkXOR-12 32868506 32.20 ns/op 31836.66 MB/s
|
||||
BenchmarkXORSafe-12 5185776 234.00 ns/op 4378.74 MB/s
|
||||
BenchmarkXORFast-12 30975679 32.50 ns/op 31525.28 MB/s
|
||||
BenchmarkXORMappedAddress_AddTo-12 21518028 54.50 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkXORMappedAddress_GetFrom-12 35597667 34.40 ns/op 0 B/op 0 allocs/op
|
||||
ok github.com/pion/stun 60.973s
|
||||
```
|
||||
|
||||
### 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
|
||||
163
server/vendor/github.com/pion/stun/addr.go
generated
vendored
Normal file
163
server/vendor/github.com/pion/stun/addr.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// MappedAddress represents MAPPED-ADDRESS attribute.
|
||||
//
|
||||
// This attribute is used only by servers for achieving backwards
|
||||
// compatibility with RFC 3489 clients.
|
||||
//
|
||||
// RFC 5389 Section 15.1
|
||||
type MappedAddress struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
// AlternateServer represents ALTERNATE-SERVER attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.11
|
||||
type AlternateServer struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
// ResponseOrigin represents RESPONSE-ORIGIN attribute.
|
||||
//
|
||||
// RFC 5780 Section 7.3
|
||||
type ResponseOrigin struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
// OtherAddress represents OTHER-ADDRESS attribute.
|
||||
//
|
||||
// RFC 5780 Section 7.4
|
||||
type OtherAddress struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
// AddTo adds ALTERNATE-SERVER attribute to message.
|
||||
func (s *AlternateServer) AddTo(m *Message) error {
|
||||
a := (*MappedAddress)(s)
|
||||
return a.AddToAs(m, AttrAlternateServer)
|
||||
}
|
||||
|
||||
// GetFrom decodes ALTERNATE-SERVER from message.
|
||||
func (s *AlternateServer) GetFrom(m *Message) error {
|
||||
a := (*MappedAddress)(s)
|
||||
return a.GetFromAs(m, AttrAlternateServer)
|
||||
}
|
||||
|
||||
func (a MappedAddress) String() string {
|
||||
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
|
||||
}
|
||||
|
||||
// GetFromAs decodes MAPPED-ADDRESS value in message m as an attribute of type t.
|
||||
func (a *MappedAddress) GetFromAs(m *Message, t AttrType) error {
|
||||
v, err := m.Get(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(v) <= 4 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
family := bin.Uint16(v[0:2])
|
||||
if family != familyIPv6 && family != familyIPv4 {
|
||||
return newDecodeErr("xor-mapped address", "family",
|
||||
fmt.Sprintf("bad value %d", family),
|
||||
)
|
||||
}
|
||||
ipLen := net.IPv4len
|
||||
if family == familyIPv6 {
|
||||
ipLen = net.IPv6len
|
||||
}
|
||||
// Ensuring len(a.IP) == ipLen and reusing a.IP.
|
||||
if len(a.IP) < ipLen {
|
||||
a.IP = a.IP[:cap(a.IP)]
|
||||
for len(a.IP) < ipLen {
|
||||
a.IP = append(a.IP, 0)
|
||||
}
|
||||
}
|
||||
a.IP = a.IP[:ipLen]
|
||||
for i := range a.IP {
|
||||
a.IP[i] = 0
|
||||
}
|
||||
a.Port = int(bin.Uint16(v[2:4]))
|
||||
copy(a.IP, v[4:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddToAs adds MAPPED-ADDRESS value to m as t attribute.
|
||||
func (a *MappedAddress) AddToAs(m *Message, t AttrType) error {
|
||||
var (
|
||||
family = familyIPv4
|
||||
ip = a.IP
|
||||
)
|
||||
if len(a.IP) == net.IPv6len {
|
||||
if isIPv4(ip) {
|
||||
ip = ip[12:16] // like in ip.To4()
|
||||
} else {
|
||||
family = familyIPv6
|
||||
}
|
||||
} else if len(ip) != net.IPv4len {
|
||||
return ErrBadIPLength
|
||||
}
|
||||
value := make([]byte, 128)
|
||||
value[0] = 0 // first 8 bits are zeroes
|
||||
bin.PutUint16(value[0:2], family)
|
||||
bin.PutUint16(value[2:4], uint16(a.Port))
|
||||
copy(value[4:], ip)
|
||||
m.Add(t, value[:4+len(ip)])
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTo adds MAPPED-ADDRESS to message.
|
||||
func (a *MappedAddress) AddTo(m *Message) error {
|
||||
return a.AddToAs(m, AttrMappedAddress)
|
||||
}
|
||||
|
||||
// GetFrom decodes MAPPED-ADDRESS from message.
|
||||
func (a *MappedAddress) GetFrom(m *Message) error {
|
||||
return a.GetFromAs(m, AttrMappedAddress)
|
||||
}
|
||||
|
||||
// AddTo adds OTHER-ADDRESS attribute to message.
|
||||
func (o *OtherAddress) AddTo(m *Message) error {
|
||||
a := (*MappedAddress)(o)
|
||||
return a.AddToAs(m, AttrOtherAddress)
|
||||
}
|
||||
|
||||
// GetFrom decodes OTHER-ADDRESS from message.
|
||||
func (o *OtherAddress) GetFrom(m *Message) error {
|
||||
a := (*MappedAddress)(o)
|
||||
return a.GetFromAs(m, AttrOtherAddress)
|
||||
}
|
||||
|
||||
func (o OtherAddress) String() string {
|
||||
return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port))
|
||||
}
|
||||
|
||||
// AddTo adds RESPONSE-ORIGIN attribute to message.
|
||||
func (o *ResponseOrigin) AddTo(m *Message) error {
|
||||
a := (*MappedAddress)(o)
|
||||
return a.AddToAs(m, AttrResponseOrigin)
|
||||
}
|
||||
|
||||
// GetFrom decodes RESPONSE-ORIGIN from message.
|
||||
func (o *ResponseOrigin) GetFrom(m *Message) error {
|
||||
a := (*MappedAddress)(o)
|
||||
return a.GetFromAs(m, AttrResponseOrigin)
|
||||
}
|
||||
|
||||
func (o ResponseOrigin) String() string {
|
||||
return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port))
|
||||
}
|
||||
233
server/vendor/github.com/pion/stun/agent.go
generated
vendored
Normal file
233
server/vendor/github.com/pion/stun/agent.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NoopHandler just discards any event.
|
||||
func NoopHandler() Handler {
|
||||
return func(e Event) {}
|
||||
}
|
||||
|
||||
// NewAgent initializes and returns new Agent with provided handler.
|
||||
// If h is nil, the NoopHandler will be used.
|
||||
func NewAgent(h Handler) *Agent {
|
||||
if h == nil {
|
||||
h = NoopHandler()
|
||||
}
|
||||
a := &Agent{
|
||||
transactions: make(map[transactionID]agentTransaction),
|
||||
handler: h,
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Agent is low-level abstraction over transaction list that
|
||||
// handles concurrency (all calls are goroutine-safe) and
|
||||
// time outs (via Collect call).
|
||||
type Agent struct {
|
||||
// transactions is map of transactions that are currently
|
||||
// in progress. Event handling is done in such way when
|
||||
// transaction is unregistered before agentTransaction access,
|
||||
// minimizing mux lock and protecting agentTransaction from
|
||||
// data races via unexpected concurrent access.
|
||||
transactions map[transactionID]agentTransaction
|
||||
closed bool // all calls are invalid if true
|
||||
mux sync.Mutex // protects transactions and closed
|
||||
handler Handler // handles transactions
|
||||
}
|
||||
|
||||
// Handler handles state changes of transaction.
|
||||
//
|
||||
// Handler is called on transaction state change.
|
||||
// Usage of e is valid only during call, user must
|
||||
// copy needed fields explicitly.
|
||||
type Handler func(e Event)
|
||||
|
||||
// Event is passed to Handler describing the transaction event.
|
||||
// Do not reuse outside Handler.
|
||||
type Event struct {
|
||||
TransactionID [TransactionIDSize]byte
|
||||
Message *Message
|
||||
Error error
|
||||
}
|
||||
|
||||
// agentTransaction represents transaction in progress.
|
||||
// Concurrent access is invalid.
|
||||
type agentTransaction struct {
|
||||
id transactionID
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrTransactionStopped indicates that transaction was manually stopped.
|
||||
ErrTransactionStopped = errors.New("transaction is stopped")
|
||||
// ErrTransactionNotExists indicates that agent failed to find transaction.
|
||||
ErrTransactionNotExists = errors.New("transaction not exists")
|
||||
// ErrTransactionExists indicates that transaction with same id is already
|
||||
// registered.
|
||||
ErrTransactionExists = errors.New("transaction exists with same id")
|
||||
)
|
||||
|
||||
// StopWithError removes transaction from list and calls handler with
|
||||
// provided error. Can return ErrTransactionNotExists and ErrAgentClosed.
|
||||
func (a *Agent) StopWithError(id [TransactionIDSize]byte, err error) error {
|
||||
a.mux.Lock()
|
||||
if a.closed {
|
||||
a.mux.Unlock()
|
||||
return ErrAgentClosed
|
||||
}
|
||||
t, exists := a.transactions[id]
|
||||
delete(a.transactions, id)
|
||||
h := a.handler
|
||||
a.mux.Unlock()
|
||||
if !exists {
|
||||
return ErrTransactionNotExists
|
||||
}
|
||||
h(Event{
|
||||
TransactionID: t.id,
|
||||
Error: err,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops transaction by id with ErrTransactionStopped, blocking
|
||||
// until handler returns.
|
||||
func (a *Agent) Stop(id [TransactionIDSize]byte) error {
|
||||
return a.StopWithError(id, ErrTransactionStopped)
|
||||
}
|
||||
|
||||
// ErrAgentClosed indicates that agent is in closed state and is unable
|
||||
// to handle transactions.
|
||||
var ErrAgentClosed = errors.New("agent is closed")
|
||||
|
||||
// Start registers transaction with provided id and deadline.
|
||||
// Could return ErrAgentClosed, ErrTransactionExists.
|
||||
//
|
||||
// Agent handler is guaranteed to be eventually called.
|
||||
func (a *Agent) Start(id [TransactionIDSize]byte, deadline time.Time) error {
|
||||
a.mux.Lock()
|
||||
defer a.mux.Unlock()
|
||||
if a.closed {
|
||||
return ErrAgentClosed
|
||||
}
|
||||
_, exists := a.transactions[id]
|
||||
if exists {
|
||||
return ErrTransactionExists
|
||||
}
|
||||
a.transactions[id] = agentTransaction{
|
||||
id: id,
|
||||
deadline: deadline,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// agentCollectCap is initial capacity for Agent.Collect slices,
|
||||
// sufficient to make function zero-alloc in most cases.
|
||||
const agentCollectCap = 100
|
||||
|
||||
// ErrTransactionTimeOut indicates that transaction has reached deadline.
|
||||
var ErrTransactionTimeOut = errors.New("transaction is timed out")
|
||||
|
||||
// Collect terminates all transactions that have deadline before provided
|
||||
// time, blocking until all handlers will process ErrTransactionTimeOut.
|
||||
// Will return ErrAgentClosed if agent is already closed.
|
||||
//
|
||||
// It is safe to call Collect concurrently but makes no sense.
|
||||
func (a *Agent) Collect(gcTime time.Time) error {
|
||||
toRemove := make([]transactionID, 0, agentCollectCap)
|
||||
a.mux.Lock()
|
||||
if a.closed {
|
||||
// Doing nothing if agent is closed.
|
||||
// All transactions should be already closed
|
||||
// during Close() call.
|
||||
a.mux.Unlock()
|
||||
return ErrAgentClosed
|
||||
}
|
||||
// Adding all transactions with deadline before gcTime
|
||||
// to toCall and toRemove slices.
|
||||
// No allocs if there are less than agentCollectCap
|
||||
// timed out transactions.
|
||||
for id, t := range a.transactions {
|
||||
if t.deadline.Before(gcTime) {
|
||||
toRemove = append(toRemove, id)
|
||||
}
|
||||
}
|
||||
// Un-registering timed out transactions.
|
||||
for _, id := range toRemove {
|
||||
delete(a.transactions, id)
|
||||
}
|
||||
// Calling handler does not require locked mutex,
|
||||
// reducing lock time.
|
||||
h := a.handler
|
||||
a.mux.Unlock()
|
||||
// Sending ErrTransactionTimeOut to handler for all transactions,
|
||||
// blocking until last one.
|
||||
event := Event{
|
||||
Error: ErrTransactionTimeOut,
|
||||
}
|
||||
for _, id := range toRemove {
|
||||
event.TransactionID = id
|
||||
h(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process incoming message, synchronously passing it to handler.
|
||||
func (a *Agent) Process(m *Message) error {
|
||||
e := Event{
|
||||
TransactionID: m.TransactionID,
|
||||
Message: m,
|
||||
}
|
||||
a.mux.Lock()
|
||||
if a.closed {
|
||||
a.mux.Unlock()
|
||||
return ErrAgentClosed
|
||||
}
|
||||
h := a.handler
|
||||
delete(a.transactions, m.TransactionID)
|
||||
a.mux.Unlock()
|
||||
h(e)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHandler sets agent handler to h.
|
||||
func (a *Agent) SetHandler(h Handler) error {
|
||||
a.mux.Lock()
|
||||
if a.closed {
|
||||
a.mux.Unlock()
|
||||
return ErrAgentClosed
|
||||
}
|
||||
a.handler = h
|
||||
a.mux.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close terminates all transactions with ErrAgentClosed and renders Agent to
|
||||
// closed state.
|
||||
func (a *Agent) Close() error {
|
||||
e := Event{
|
||||
Error: ErrAgentClosed,
|
||||
}
|
||||
a.mux.Lock()
|
||||
if a.closed {
|
||||
a.mux.Unlock()
|
||||
return ErrAgentClosed
|
||||
}
|
||||
for _, t := range a.transactions {
|
||||
e.TransactionID = t.id
|
||||
a.handler(e)
|
||||
}
|
||||
a.transactions = nil
|
||||
a.closed = true
|
||||
a.handler = nil
|
||||
a.mux.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
type transactionID [TransactionIDSize]byte
|
||||
254
server/vendor/github.com/pion/stun/attributes.go
generated
vendored
Normal file
254
server/vendor/github.com/pion/stun/attributes.go
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Attributes is list of message attributes.
|
||||
type Attributes []RawAttribute
|
||||
|
||||
// Get returns first attribute from list by the type.
|
||||
// If attribute is present the RawAttribute is returned and the
|
||||
// boolean is true. Otherwise the returned RawAttribute will be
|
||||
// empty and boolean will be false.
|
||||
func (a Attributes) Get(t AttrType) (RawAttribute, bool) {
|
||||
for _, candidate := range a {
|
||||
if candidate.Type == t {
|
||||
return candidate, true
|
||||
}
|
||||
}
|
||||
return RawAttribute{}, false
|
||||
}
|
||||
|
||||
// AttrType is attribute type.
|
||||
type AttrType uint16
|
||||
|
||||
// Required returns true if type is from comprehension-required range (0x0000-0x7FFF).
|
||||
func (t AttrType) Required() bool {
|
||||
return t <= 0x7FFF
|
||||
}
|
||||
|
||||
// Optional returns true if type is from comprehension-optional range (0x8000-0xFFFF).
|
||||
func (t AttrType) Optional() bool {
|
||||
return t >= 0x8000
|
||||
}
|
||||
|
||||
// Attributes from comprehension-required range (0x0000-0x7FFF).
|
||||
const (
|
||||
AttrMappedAddress AttrType = 0x0001 // MAPPED-ADDRESS
|
||||
AttrUsername AttrType = 0x0006 // USERNAME
|
||||
AttrMessageIntegrity AttrType = 0x0008 // MESSAGE-INTEGRITY
|
||||
AttrErrorCode AttrType = 0x0009 // ERROR-CODE
|
||||
AttrUnknownAttributes AttrType = 0x000A // UNKNOWN-ATTRIBUTES
|
||||
AttrRealm AttrType = 0x0014 // REALM
|
||||
AttrNonce AttrType = 0x0015 // NONCE
|
||||
AttrXORMappedAddress AttrType = 0x0020 // XOR-MAPPED-ADDRESS
|
||||
)
|
||||
|
||||
// Attributes from comprehension-optional range (0x8000-0xFFFF).
|
||||
const (
|
||||
AttrSoftware AttrType = 0x8022 // SOFTWARE
|
||||
AttrAlternateServer AttrType = 0x8023 // ALTERNATE-SERVER
|
||||
AttrFingerprint AttrType = 0x8028 // FINGERPRINT
|
||||
)
|
||||
|
||||
// Attributes from RFC 5245 ICE.
|
||||
const (
|
||||
AttrPriority AttrType = 0x0024 // PRIORITY
|
||||
AttrUseCandidate AttrType = 0x0025 // USE-CANDIDATE
|
||||
AttrICEControlled AttrType = 0x8029 // ICE-CONTROLLED
|
||||
AttrICEControlling AttrType = 0x802A // ICE-CONTROLLING
|
||||
)
|
||||
|
||||
// Attributes from RFC 5766 TURN.
|
||||
const (
|
||||
AttrChannelNumber AttrType = 0x000C // CHANNEL-NUMBER
|
||||
AttrLifetime AttrType = 0x000D // LIFETIME
|
||||
AttrXORPeerAddress AttrType = 0x0012 // XOR-PEER-ADDRESS
|
||||
AttrData AttrType = 0x0013 // DATA
|
||||
AttrXORRelayedAddress AttrType = 0x0016 // XOR-RELAYED-ADDRESS
|
||||
AttrEvenPort AttrType = 0x0018 // EVEN-PORT
|
||||
AttrRequestedTransport AttrType = 0x0019 // REQUESTED-TRANSPORT
|
||||
AttrDontFragment AttrType = 0x001A // DONT-FRAGMENT
|
||||
AttrReservationToken AttrType = 0x0022 // RESERVATION-TOKEN
|
||||
)
|
||||
|
||||
// Attributes from RFC 5780 NAT Behavior Discovery
|
||||
const (
|
||||
AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST
|
||||
AttrPadding AttrType = 0x0026 // PADDING
|
||||
AttrResponsePort AttrType = 0x0027 // RESPONSE-PORT
|
||||
AttrCacheTimeout AttrType = 0x8027 // CACHE-TIMEOUT
|
||||
AttrResponseOrigin AttrType = 0x802b // RESPONSE-ORIGIN
|
||||
AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS
|
||||
)
|
||||
|
||||
// Attributes from RFC 3489, removed by RFC 5389,
|
||||
//
|
||||
// but still used by RFC5389-implementing software like Vovida.org, reTURNServer, etc.
|
||||
const (
|
||||
AttrSourceAddress AttrType = 0x0004 // SOURCE-ADDRESS
|
||||
AttrChangedAddress AttrType = 0x0005 // CHANGED-ADDRESS
|
||||
)
|
||||
|
||||
// Attributes from RFC 6062 TURN Extensions for TCP Allocations.
|
||||
const (
|
||||
AttrConnectionID AttrType = 0x002a // CONNECTION-ID
|
||||
)
|
||||
|
||||
// Attributes from RFC 6156 TURN IPv6.
|
||||
const (
|
||||
AttrRequestedAddressFamily AttrType = 0x0017 // REQUESTED-ADDRESS-FAMILY
|
||||
)
|
||||
|
||||
// Attributes from An Origin Attribute for the STUN Protocol.
|
||||
const (
|
||||
AttrOrigin AttrType = 0x802F
|
||||
)
|
||||
|
||||
// Attributes from RFC 8489 STUN.
|
||||
const (
|
||||
AttrMessageIntegritySHA256 AttrType = 0x001C // MESSAGE-INTEGRITY-SHA256
|
||||
AttrPasswordAlgorithm AttrType = 0x001D // PASSWORD-ALGORITHM
|
||||
AttrUserhash AttrType = 0x001E // USERHASH
|
||||
AttrPasswordAlgorithms AttrType = 0x8002 // PASSWORD-ALGORITHMS
|
||||
AttrAlternateDomain AttrType = 0x8003 // ALTERNATE-DOMAIN
|
||||
)
|
||||
|
||||
// Value returns uint16 representation of attribute type.
|
||||
func (t AttrType) Value() uint16 {
|
||||
return uint16(t)
|
||||
}
|
||||
|
||||
func attrNames() map[AttrType]string {
|
||||
return map[AttrType]string{
|
||||
AttrMappedAddress: "MAPPED-ADDRESS",
|
||||
AttrUsername: "USERNAME",
|
||||
AttrErrorCode: "ERROR-CODE",
|
||||
AttrMessageIntegrity: "MESSAGE-INTEGRITY",
|
||||
AttrUnknownAttributes: "UNKNOWN-ATTRIBUTES",
|
||||
AttrRealm: "REALM",
|
||||
AttrNonce: "NONCE",
|
||||
AttrXORMappedAddress: "XOR-MAPPED-ADDRESS",
|
||||
AttrSoftware: "SOFTWARE",
|
||||
AttrAlternateServer: "ALTERNATE-SERVER",
|
||||
AttrFingerprint: "FINGERPRINT",
|
||||
AttrPriority: "PRIORITY",
|
||||
AttrUseCandidate: "USE-CANDIDATE",
|
||||
AttrICEControlled: "ICE-CONTROLLED",
|
||||
AttrICEControlling: "ICE-CONTROLLING",
|
||||
AttrChannelNumber: "CHANNEL-NUMBER",
|
||||
AttrLifetime: "LIFETIME",
|
||||
AttrXORPeerAddress: "XOR-PEER-ADDRESS",
|
||||
AttrData: "DATA",
|
||||
AttrXORRelayedAddress: "XOR-RELAYED-ADDRESS",
|
||||
AttrEvenPort: "EVEN-PORT",
|
||||
AttrRequestedTransport: "REQUESTED-TRANSPORT",
|
||||
AttrDontFragment: "DONT-FRAGMENT",
|
||||
AttrReservationToken: "RESERVATION-TOKEN",
|
||||
AttrConnectionID: "CONNECTION-ID",
|
||||
AttrRequestedAddressFamily: "REQUESTED-ADDRESS-FAMILY",
|
||||
AttrMessageIntegritySHA256: "MESSAGE-INTEGRITY-SHA256",
|
||||
AttrPasswordAlgorithm: "PASSWORD-ALGORITHM",
|
||||
AttrUserhash: "USERHASH",
|
||||
AttrPasswordAlgorithms: "PASSWORD-ALGORITHMS",
|
||||
AttrAlternateDomain: "ALTERNATE-DOMAIN",
|
||||
}
|
||||
}
|
||||
|
||||
func (t AttrType) String() string {
|
||||
s, ok := attrNames()[t]
|
||||
if !ok {
|
||||
// Just return hex representation of unknown attribute type.
|
||||
return fmt.Sprintf("0x%x", uint16(t))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RawAttribute is a Type-Length-Value (TLV) object that
|
||||
// can be added to a STUN message. Attributes are divided into two
|
||||
// types: comprehension-required and comprehension-optional. STUN
|
||||
// agents can safely ignore comprehension-optional attributes they
|
||||
// don't understand, but cannot successfully process a message if it
|
||||
// contains comprehension-required attributes that are not
|
||||
// understood.
|
||||
type RawAttribute struct {
|
||||
Type AttrType
|
||||
Length uint16 // ignored while encoding
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// AddTo implements Setter, adding attribute as a.Type with a.Value and ignoring
|
||||
// the Length field.
|
||||
func (a RawAttribute) AddTo(m *Message) error {
|
||||
m.Add(a.Type, a.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true if a == b.
|
||||
func (a RawAttribute) Equal(b RawAttribute) bool {
|
||||
if a.Type != b.Type {
|
||||
return false
|
||||
}
|
||||
if a.Length != b.Length {
|
||||
return false
|
||||
}
|
||||
if len(b.Value) != len(a.Value) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a.Value {
|
||||
if b.Value[i] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a RawAttribute) String() string {
|
||||
return fmt.Sprintf("%s: 0x%x", a.Type, a.Value)
|
||||
}
|
||||
|
||||
// ErrAttributeNotFound means that attribute with provided attribute
|
||||
// type does not exist in message.
|
||||
var ErrAttributeNotFound = errors.New("attribute not found")
|
||||
|
||||
// Get returns byte slice that represents attribute value,
|
||||
// if there is no attribute with such type,
|
||||
// ErrAttributeNotFound is returned.
|
||||
func (m *Message) Get(t AttrType) ([]byte, error) {
|
||||
v, ok := m.Attributes.Get(t)
|
||||
if !ok {
|
||||
return nil, ErrAttributeNotFound
|
||||
}
|
||||
return v.Value, nil
|
||||
}
|
||||
|
||||
// STUN aligns attributes on 32-bit boundaries, attributes whose content
|
||||
// is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of
|
||||
// padding so that its value contains a multiple of 4 bytes. The
|
||||
// padding bits are ignored, and may be any value.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5389#section-15
|
||||
const padding = 4
|
||||
|
||||
func nearestPaddedValueLength(l int) int {
|
||||
n := padding * (l / padding)
|
||||
if n < l {
|
||||
n += padding
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// This method converts uint16 vlue to AttrType. If it finds an old attribute
|
||||
// type value, it also translates it to the new value to enable backward
|
||||
// compatibility. (See: https://github.com/pion/stun/issues/21)
|
||||
func compatAttrType(val uint16) AttrType {
|
||||
if val == 0x8020 { // draft-ietf-behave-rfc3489bis-02, MS-TURN
|
||||
return AttrXORMappedAddress // new: 0x0020 (from draft-ietf-behave-rfc3489bis-03 on)
|
||||
}
|
||||
return AttrType(val)
|
||||
}
|
||||
37
server/vendor/github.com/pion/stun/attributes_debug.go
generated
vendored
Normal file
37
server/vendor/github.com/pion/stun/attributes_debug.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package stun
|
||||
|
||||
import "fmt"
|
||||
|
||||
// AttrOverflowErr occurs when len(v) > Max.
|
||||
type AttrOverflowErr struct {
|
||||
Type AttrType
|
||||
Max int
|
||||
Got int
|
||||
}
|
||||
|
||||
func (e AttrOverflowErr) Error() string {
|
||||
return fmt.Sprintf("incorrect length of %s attribute: %d exceeds maximum %d",
|
||||
e.Type, e.Got, e.Max,
|
||||
)
|
||||
}
|
||||
|
||||
// AttrLengthErr means that length for attribute is invalid.
|
||||
type AttrLengthErr struct {
|
||||
Attr AttrType
|
||||
Got int
|
||||
Expected int
|
||||
}
|
||||
|
||||
func (e AttrLengthErr) Error() string {
|
||||
return fmt.Sprintf("incorrect length of %s attribute: got %d, expected %d",
|
||||
e.Attr,
|
||||
e.Got,
|
||||
e.Expected,
|
||||
)
|
||||
}
|
||||
53
server/vendor/github.com/pion/stun/checks.go
generated
vendored
Normal file
53
server/vendor/github.com/pion/stun/checks.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !debug
|
||||
// +build !debug
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pion/stun/internal/hmac"
|
||||
)
|
||||
|
||||
// CheckSize returns ErrAttrSizeInvalid if got is not equal to expected.
|
||||
func CheckSize(_ AttrType, got, expected int) error {
|
||||
if got == expected {
|
||||
return nil
|
||||
}
|
||||
return ErrAttributeSizeInvalid
|
||||
}
|
||||
|
||||
func checkHMAC(got, expected []byte) error {
|
||||
if hmac.Equal(got, expected) {
|
||||
return nil
|
||||
}
|
||||
return ErrIntegrityMismatch
|
||||
}
|
||||
|
||||
func checkFingerprint(got, expected uint32) error {
|
||||
if got == expected {
|
||||
return nil
|
||||
}
|
||||
return ErrFingerprintMismatch
|
||||
}
|
||||
|
||||
// IsAttrSizeInvalid returns true if error means that attribute size is invalid.
|
||||
func IsAttrSizeInvalid(err error) bool {
|
||||
return errors.Is(err, ErrAttributeSizeInvalid)
|
||||
}
|
||||
|
||||
// CheckOverflow returns ErrAttributeSizeOverflow if got is bigger that max.
|
||||
func CheckOverflow(_ AttrType, got, max int) error {
|
||||
if got <= max {
|
||||
return nil
|
||||
}
|
||||
return ErrAttributeSizeOverflow
|
||||
}
|
||||
|
||||
// IsAttrSizeOverflow returns true if error means that attribute size is too big.
|
||||
func IsAttrSizeOverflow(err error) bool {
|
||||
return errors.Is(err, ErrAttributeSizeOverflow)
|
||||
}
|
||||
65
server/vendor/github.com/pion/stun/checks_debug.go
generated
vendored
Normal file
65
server/vendor/github.com/pion/stun/checks_debug.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package stun
|
||||
|
||||
import "github.com/pion/stun/internal/hmac"
|
||||
|
||||
// CheckSize returns *AttrLengthError if got is not equal to expected.
|
||||
func CheckSize(a AttrType, got, expected int) error {
|
||||
if got == expected {
|
||||
return nil
|
||||
}
|
||||
return &AttrLengthErr{
|
||||
Got: got,
|
||||
Expected: expected,
|
||||
Attr: a,
|
||||
}
|
||||
}
|
||||
|
||||
func checkHMAC(got, expected []byte) error {
|
||||
if hmac.Equal(got, expected) {
|
||||
return nil
|
||||
}
|
||||
return &IntegrityErr{
|
||||
Expected: expected,
|
||||
Actual: got,
|
||||
}
|
||||
}
|
||||
|
||||
func checkFingerprint(got, expected uint32) error {
|
||||
if got == expected {
|
||||
return nil
|
||||
}
|
||||
return &CRCMismatch{
|
||||
Actual: got,
|
||||
Expected: expected,
|
||||
}
|
||||
}
|
||||
|
||||
// IsAttrSizeInvalid returns true if error means that attribute size is invalid.
|
||||
func IsAttrSizeInvalid(err error) bool {
|
||||
_, ok := err.(*AttrLengthErr)
|
||||
return ok
|
||||
}
|
||||
|
||||
// CheckOverflow returns *AttrOverflowErr if got is bigger that max.
|
||||
func CheckOverflow(t AttrType, got, max int) error {
|
||||
if got <= max {
|
||||
return nil
|
||||
}
|
||||
return &AttrOverflowErr{
|
||||
Type: t,
|
||||
Got: got,
|
||||
Max: max,
|
||||
}
|
||||
}
|
||||
|
||||
// IsAttrSizeOverflow returns true if error means that attribute size is too big.
|
||||
func IsAttrSizeOverflow(err error) bool {
|
||||
_, ok := err.(*AttrOverflowErr)
|
||||
return ok
|
||||
}
|
||||
722
server/vendor/github.com/pion/stun/client.go
generated
vendored
Normal file
722
server/vendor/github.com/pion/stun/client.go
generated
vendored
Normal file
@@ -0,0 +1,722 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2"
|
||||
"github.com/pion/transport/v2"
|
||||
"github.com/pion/transport/v2/stdnet"
|
||||
)
|
||||
|
||||
// ErrUnsupportedURI is an error thrown if the user passes an unsupported STUN or TURN URI
|
||||
var ErrUnsupportedURI = fmt.Errorf("invalid schema or transport")
|
||||
|
||||
// Dial connects to the address on the named network and then
|
||||
// initializes Client on that connection, returning error if any.
|
||||
func Dial(network, address string) (*Client, error) {
|
||||
conn, err := net.Dial(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewClient(conn)
|
||||
}
|
||||
|
||||
// DialConfig is used to pass configuration to DialURI()
|
||||
type DialConfig struct {
|
||||
DTLSConfig dtls.Config
|
||||
TLSConfig tls.Config
|
||||
|
||||
Net transport.Net
|
||||
}
|
||||
|
||||
// DialURI connect to the STUN/TURN URI and then
|
||||
// initializes Client on that connection, returning error if any.
|
||||
func DialURI(uri *URI, cfg *DialConfig) (*Client, error) {
|
||||
var conn Connection
|
||||
var err error
|
||||
|
||||
nw := cfg.Net
|
||||
if nw == nil {
|
||||
nw, err = stdnet.NewNet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create net: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port))
|
||||
|
||||
switch {
|
||||
case uri.Scheme == SchemeTypeSTUN:
|
||||
if conn, err = nw.Dial("udp", addr); err != nil {
|
||||
return nil, fmt.Errorf("failed to listen: %w", err)
|
||||
}
|
||||
|
||||
case uri.Scheme == SchemeTypeTURN:
|
||||
network := "udp" //nolint:goconst
|
||||
if uri.Proto == ProtoTypeTCP {
|
||||
network = "tcp" //nolint:goconst
|
||||
}
|
||||
|
||||
if conn, err = nw.Dial(network, addr); err != nil {
|
||||
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||
}
|
||||
|
||||
case uri.Scheme == SchemeTypeTURNS && uri.Proto == ProtoTypeUDP:
|
||||
dtlsCfg := cfg.DTLSConfig // Copy
|
||||
dtlsCfg.ServerName = uri.Host
|
||||
|
||||
udpConn, err := nw.Dial("udp", addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||
}
|
||||
|
||||
if conn, err = dtls.Client(udpConn, &dtlsCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to '%s': %w", addr, err)
|
||||
}
|
||||
|
||||
case (uri.Scheme == SchemeTypeTURNS || uri.Scheme == SchemeTypeSTUNS) && uri.Proto == ProtoTypeTCP:
|
||||
tlsCfg := cfg.TLSConfig //nolint:govet
|
||||
tlsCfg.ServerName = uri.Host
|
||||
|
||||
tcpConn, err := nw.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||
}
|
||||
|
||||
conn = tls.Client(tcpConn, &tlsCfg)
|
||||
|
||||
default:
|
||||
return nil, ErrUnsupportedURI
|
||||
}
|
||||
|
||||
return NewClient(conn)
|
||||
}
|
||||
|
||||
// ErrNoConnection means that ClientOptions.Connection is nil.
|
||||
var ErrNoConnection = errors.New("no connection provided")
|
||||
|
||||
// ClientOption sets some client option.
|
||||
type ClientOption func(c *Client)
|
||||
|
||||
// WithHandler sets client handler which is called if Agent emits the Event
|
||||
// with TransactionID that is not currently registered by Client.
|
||||
// Useful for handling Data indications from TURN server.
|
||||
func WithHandler(h Handler) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.handler = h
|
||||
}
|
||||
}
|
||||
|
||||
// WithRTO sets client RTO as defined in STUN RFC.
|
||||
func WithRTO(rto time.Duration) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.rto = int64(rto)
|
||||
}
|
||||
}
|
||||
|
||||
// WithClock sets Clock of client, the source of current time.
|
||||
// Also clock is passed to default collector if set.
|
||||
func WithClock(clock Clock) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.clock = clock
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeoutRate sets RTO timer minimum resolution.
|
||||
func WithTimeoutRate(d time.Duration) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.rtoRate = d
|
||||
}
|
||||
}
|
||||
|
||||
// WithAgent sets client STUN agent.
|
||||
//
|
||||
// Defaults to agent implementation in current package,
|
||||
// see agent.go.
|
||||
func WithAgent(a ClientAgent) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.a = a
|
||||
}
|
||||
}
|
||||
|
||||
// WithCollector rests client timeout collector, the implementation
|
||||
// of ticker which calls function on each tick.
|
||||
func WithCollector(coll Collector) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.collector = coll
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoConnClose prevents client from closing underlying connection when
|
||||
// the Close() method is called.
|
||||
func WithNoConnClose() ClientOption {
|
||||
return func(c *Client) {
|
||||
c.closeConn = false
|
||||
}
|
||||
}
|
||||
|
||||
// WithNoRetransmit disables retransmissions and sets RTO to
|
||||
// defaultMaxAttempts * defaultRTO which will be effectively time out
|
||||
// if not set.
|
||||
//
|
||||
// Useful for TCP connections where transport handles RTO.
|
||||
func WithNoRetransmit(c *Client) {
|
||||
c.maxAttempts = 0
|
||||
if c.rto == 0 {
|
||||
c.rto = defaultMaxAttempts * int64(defaultRTO)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
defaultTimeoutRate = time.Millisecond * 5
|
||||
defaultRTO = time.Millisecond * 300
|
||||
defaultMaxAttempts = 7
|
||||
)
|
||||
|
||||
// NewClient initializes new Client from provided options,
|
||||
// starting internal goroutines and using default options fields
|
||||
// if necessary. Call Close method after using Client to close conn and
|
||||
// release resources.
|
||||
//
|
||||
// The conn will be closed on Close call. Use WithNoConnClose option to
|
||||
// prevent that.
|
||||
//
|
||||
// Note that user should handle the protocol multiplexing, client does not
|
||||
// provide any API for it, so if you need to read application data, wrap the
|
||||
// connection with your (de-)multiplexer and pass the wrapper as conn.
|
||||
func NewClient(conn Connection, options ...ClientOption) (*Client, error) {
|
||||
c := &Client{
|
||||
close: make(chan struct{}),
|
||||
c: conn,
|
||||
clock: systemClock(),
|
||||
rto: int64(defaultRTO),
|
||||
rtoRate: defaultTimeoutRate,
|
||||
t: make(map[transactionID]*clientTransaction, 100),
|
||||
maxAttempts: defaultMaxAttempts,
|
||||
closeConn: true,
|
||||
}
|
||||
for _, o := range options {
|
||||
o(c)
|
||||
}
|
||||
if c.c == nil {
|
||||
return nil, ErrNoConnection
|
||||
}
|
||||
if c.a == nil {
|
||||
c.a = NewAgent(nil)
|
||||
}
|
||||
if err := c.a.SetHandler(c.handleAgentCallback); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.collector == nil {
|
||||
c.collector = &tickerCollector{
|
||||
close: make(chan struct{}),
|
||||
clock: c.clock,
|
||||
}
|
||||
}
|
||||
if err := c.collector.Start(c.rtoRate, func(t time.Time) {
|
||||
closedOrPanic(c.a.Collect(t))
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.wg.Add(1)
|
||||
go c.readUntilClosed()
|
||||
runtime.SetFinalizer(c, clientFinalizer)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func clientFinalizer(c *Client) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
err := c.Close()
|
||||
if errors.Is(err, ErrClientClosed) {
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
log.Println("client: called finalizer on non-closed client") // nolint
|
||||
return
|
||||
}
|
||||
log.Println("client: called finalizer on non-closed client:", err) // nolint
|
||||
}
|
||||
|
||||
// Connection wraps Reader, Writer and Closer interfaces.
|
||||
type Connection interface {
|
||||
io.Reader
|
||||
io.Writer
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// ClientAgent is Agent implementation that is used by Client to
|
||||
// process transactions.
|
||||
type ClientAgent interface {
|
||||
Process(*Message) error
|
||||
Close() error
|
||||
Start(id [TransactionIDSize]byte, deadline time.Time) error
|
||||
Stop(id [TransactionIDSize]byte) error
|
||||
Collect(time.Time) error
|
||||
SetHandler(h Handler) error
|
||||
}
|
||||
|
||||
// Client simulates "connection" to STUN server.
|
||||
type Client struct {
|
||||
rto int64 // time.Duration
|
||||
a ClientAgent
|
||||
c Connection
|
||||
close chan struct{}
|
||||
rtoRate time.Duration
|
||||
maxAttempts int32
|
||||
closed bool
|
||||
closeConn bool // should call c.Close() while closing
|
||||
wg sync.WaitGroup
|
||||
clock Clock
|
||||
handler Handler
|
||||
collector Collector
|
||||
t map[transactionID]*clientTransaction
|
||||
|
||||
// mux guards closed and t
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// clientTransaction represents transaction in progress.
|
||||
// If transaction is succeed or failed, f will be called
|
||||
// provided by event.
|
||||
// Concurrent access is invalid.
|
||||
type clientTransaction struct {
|
||||
id transactionID
|
||||
attempt int32
|
||||
calls int32
|
||||
h Handler
|
||||
start time.Time
|
||||
rto time.Duration
|
||||
raw []byte
|
||||
}
|
||||
|
||||
func (t *clientTransaction) handle(e Event) {
|
||||
if atomic.AddInt32(&t.calls, 1) == 1 {
|
||||
t.h(e)
|
||||
}
|
||||
}
|
||||
|
||||
var clientTransactionPool = &sync.Pool{ //nolint:gochecknoglobals
|
||||
New: func() interface{} {
|
||||
return &clientTransaction{
|
||||
raw: make([]byte, 1500),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func acquireClientTransaction() *clientTransaction {
|
||||
return clientTransactionPool.Get().(*clientTransaction) //nolint:forcetypeassert
|
||||
}
|
||||
|
||||
func putClientTransaction(t *clientTransaction) {
|
||||
t.raw = t.raw[:0]
|
||||
t.start = time.Time{}
|
||||
t.attempt = 0
|
||||
t.id = transactionID{}
|
||||
clientTransactionPool.Put(t)
|
||||
}
|
||||
|
||||
func (t *clientTransaction) nextTimeout(now time.Time) time.Time {
|
||||
return now.Add(time.Duration(t.attempt+1) * t.rto)
|
||||
}
|
||||
|
||||
// start registers transaction.
|
||||
//
|
||||
// Could return ErrClientClosed, ErrTransactionExists.
|
||||
func (c *Client) start(t *clientTransaction) error {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
if c.closed {
|
||||
return ErrClientClosed
|
||||
}
|
||||
_, exists := c.t[t.id]
|
||||
if exists {
|
||||
return ErrTransactionExists
|
||||
}
|
||||
c.t[t.id] = t
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clock abstracts the source of current time.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
type systemClockService struct{}
|
||||
|
||||
func (systemClockService) Now() time.Time { return time.Now() }
|
||||
|
||||
func systemClock() systemClockService {
|
||||
return systemClockService{}
|
||||
}
|
||||
|
||||
// SetRTO sets current RTO value.
|
||||
func (c *Client) SetRTO(rto time.Duration) {
|
||||
atomic.StoreInt64(&c.rto, int64(rto))
|
||||
}
|
||||
|
||||
// StopErr occurs when Client fails to stop transaction while
|
||||
// processing error.
|
||||
//
|
||||
//nolint:errname
|
||||
type StopErr struct {
|
||||
Err error // value returned by Stop()
|
||||
Cause error // error that caused Stop() call
|
||||
}
|
||||
|
||||
func (e StopErr) Error() string {
|
||||
return fmt.Sprintf("error while stopping due to %s: %s", sprintErr(e.Cause), sprintErr(e.Err))
|
||||
}
|
||||
|
||||
// CloseErr indicates client close failure.
|
||||
//
|
||||
//nolint:errname
|
||||
type CloseErr struct {
|
||||
AgentErr error
|
||||
ConnectionErr error
|
||||
}
|
||||
|
||||
func sprintErr(err error) string {
|
||||
if err == nil {
|
||||
return "<nil>" //nolint:goconst
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func (c CloseErr) Error() string {
|
||||
return fmt.Sprintf("failed to close: %s (connection), %s (agent)", sprintErr(c.ConnectionErr), sprintErr(c.AgentErr))
|
||||
}
|
||||
|
||||
func (c *Client) readUntilClosed() {
|
||||
defer c.wg.Done()
|
||||
m := new(Message)
|
||||
m.Raw = make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
case <-c.close:
|
||||
return
|
||||
default:
|
||||
}
|
||||
_, err := m.ReadFrom(c.c)
|
||||
if err == nil {
|
||||
if pErr := c.a.Process(m); errors.Is(pErr, ErrAgentClosed) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func closedOrPanic(err error) {
|
||||
if err == nil || errors.Is(err, ErrAgentClosed) {
|
||||
return
|
||||
}
|
||||
panic(err) //nolint
|
||||
}
|
||||
|
||||
type tickerCollector struct {
|
||||
close chan struct{}
|
||||
wg sync.WaitGroup
|
||||
clock Clock
|
||||
}
|
||||
|
||||
// Collector calls function f with constant rate.
|
||||
//
|
||||
// The simple Collector is ticker which calls function on each tick.
|
||||
type Collector interface {
|
||||
Start(rate time.Duration, f func(now time.Time)) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
func (a *tickerCollector) Start(rate time.Duration, f func(now time.Time)) error {
|
||||
t := time.NewTicker(rate)
|
||||
a.wg.Add(1)
|
||||
go func() {
|
||||
defer a.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-a.close:
|
||||
t.Stop()
|
||||
return
|
||||
case <-t.C:
|
||||
f(a.clock.Now())
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *tickerCollector) Close() error {
|
||||
close(a.close)
|
||||
a.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrClientClosed indicates that client is closed.
|
||||
var ErrClientClosed = errors.New("client is closed")
|
||||
|
||||
// Close stops internal connection and agent, returning CloseErr on error.
|
||||
func (c *Client) Close() error {
|
||||
if err := c.checkInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.mux.Lock()
|
||||
if c.closed {
|
||||
c.mux.Unlock()
|
||||
return ErrClientClosed
|
||||
}
|
||||
c.closed = true
|
||||
c.mux.Unlock()
|
||||
if closeErr := c.collector.Close(); closeErr != nil {
|
||||
return closeErr
|
||||
}
|
||||
var connErr error
|
||||
agentErr := c.a.Close()
|
||||
if c.closeConn {
|
||||
connErr = c.c.Close()
|
||||
}
|
||||
close(c.close)
|
||||
c.wg.Wait()
|
||||
if agentErr == nil && connErr == nil {
|
||||
return nil
|
||||
}
|
||||
return CloseErr{
|
||||
AgentErr: agentErr,
|
||||
ConnectionErr: connErr,
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate sends indication m to server. Shorthand to Start call
|
||||
// with zero deadline and callback.
|
||||
func (c *Client) Indicate(m *Message) error {
|
||||
return c.Start(m, nil)
|
||||
}
|
||||
|
||||
// callbackWaitHandler blocks on wait() call until callback is called.
|
||||
type callbackWaitHandler struct {
|
||||
handler Handler
|
||||
callback func(event Event)
|
||||
cond *sync.Cond
|
||||
processed bool
|
||||
}
|
||||
|
||||
func (s *callbackWaitHandler) HandleEvent(e Event) {
|
||||
s.cond.L.Lock()
|
||||
if s.callback == nil {
|
||||
panic("s.callback is nil") //nolint
|
||||
}
|
||||
s.callback(e)
|
||||
s.processed = true
|
||||
s.cond.Broadcast()
|
||||
s.cond.L.Unlock()
|
||||
}
|
||||
|
||||
func (s *callbackWaitHandler) wait() {
|
||||
s.cond.L.Lock()
|
||||
for !s.processed {
|
||||
s.cond.Wait()
|
||||
}
|
||||
s.processed = false
|
||||
s.callback = nil
|
||||
s.cond.L.Unlock()
|
||||
}
|
||||
|
||||
func (s *callbackWaitHandler) setCallback(f func(event Event)) {
|
||||
if f == nil {
|
||||
panic("f is nil") //nolint
|
||||
}
|
||||
s.cond.L.Lock()
|
||||
s.callback = f
|
||||
if s.handler == nil {
|
||||
s.handler = s.HandleEvent
|
||||
}
|
||||
s.cond.L.Unlock()
|
||||
}
|
||||
|
||||
var callbackWaitHandlerPool = sync.Pool{ //nolint:gochecknoglobals
|
||||
New: func() interface{} {
|
||||
return &callbackWaitHandler{
|
||||
cond: sync.NewCond(new(sync.Mutex)),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ErrClientNotInitialized means that client connection or agent is nil.
|
||||
var ErrClientNotInitialized = errors.New("client not initialized")
|
||||
|
||||
func (c *Client) checkInit() error {
|
||||
if c == nil || c.c == nil || c.a == nil || c.close == nil {
|
||||
return ErrClientNotInitialized
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do is Start wrapper that waits until callback is called. If no callback
|
||||
// provided, Indicate is called instead.
|
||||
//
|
||||
// Do has cpu overhead due to blocking, see BenchmarkClient_Do.
|
||||
// Use Start method for less overhead.
|
||||
func (c *Client) Do(m *Message, f func(Event)) error {
|
||||
if err := c.checkInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
if f == nil {
|
||||
return c.Indicate(m)
|
||||
}
|
||||
h := callbackWaitHandlerPool.Get().(*callbackWaitHandler) //nolint:forcetypeassert
|
||||
h.setCallback(f)
|
||||
defer func() {
|
||||
callbackWaitHandlerPool.Put(h)
|
||||
}()
|
||||
if err := c.Start(m, h.handler); err != nil {
|
||||
return err
|
||||
}
|
||||
h.wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) delete(id transactionID) {
|
||||
c.mux.Lock()
|
||||
if c.t != nil {
|
||||
delete(c.t, id)
|
||||
}
|
||||
c.mux.Unlock()
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var bufferPool = &sync.Pool{ //nolint:gochecknoglobals
|
||||
New: func() interface{} {
|
||||
return &buffer{buf: make([]byte, 2048)}
|
||||
},
|
||||
}
|
||||
|
||||
func (c *Client) handleAgentCallback(e Event) {
|
||||
c.mux.Lock()
|
||||
if c.closed {
|
||||
c.mux.Unlock()
|
||||
return
|
||||
}
|
||||
t, found := c.t[e.TransactionID]
|
||||
if found {
|
||||
delete(c.t, t.id)
|
||||
}
|
||||
c.mux.Unlock()
|
||||
if !found {
|
||||
if c.handler != nil && !errors.Is(e.Error, ErrTransactionStopped) {
|
||||
c.handler(e)
|
||||
}
|
||||
// Ignoring.
|
||||
return
|
||||
}
|
||||
if atomic.LoadInt32(&c.maxAttempts) <= t.attempt || e.Error == nil {
|
||||
// Transaction completed.
|
||||
t.handle(e)
|
||||
putClientTransaction(t)
|
||||
return
|
||||
}
|
||||
// Doing re-transmission.
|
||||
t.attempt++
|
||||
b := bufferPool.Get().(*buffer) //nolint:forcetypeassert
|
||||
b.buf = b.buf[:copy(b.buf[:cap(b.buf)], t.raw)]
|
||||
defer bufferPool.Put(b)
|
||||
var (
|
||||
now = c.clock.Now()
|
||||
timeOut = t.nextTimeout(now)
|
||||
id = t.id
|
||||
)
|
||||
// Starting client transaction.
|
||||
if startErr := c.start(t); startErr != nil {
|
||||
c.delete(id)
|
||||
e.Error = startErr
|
||||
t.handle(e)
|
||||
putClientTransaction(t)
|
||||
return
|
||||
}
|
||||
// Starting agent transaction.
|
||||
if startErr := c.a.Start(id, timeOut); startErr != nil {
|
||||
c.delete(id)
|
||||
e.Error = startErr
|
||||
t.handle(e)
|
||||
putClientTransaction(t)
|
||||
return
|
||||
}
|
||||
// Writing message to connection again.
|
||||
_, writeErr := c.c.Write(b.buf)
|
||||
if writeErr != nil {
|
||||
c.delete(id)
|
||||
e.Error = writeErr
|
||||
// Stopping agent transaction instead of waiting until it's deadline.
|
||||
// This will call handleAgentCallback with "ErrTransactionStopped" error
|
||||
// which will be ignored.
|
||||
if stopErr := c.a.Stop(id); stopErr != nil {
|
||||
// Failed to stop agent transaction. Wrapping the error in StopError.
|
||||
e.Error = StopErr{
|
||||
Err: stopErr,
|
||||
Cause: writeErr,
|
||||
}
|
||||
}
|
||||
t.handle(e)
|
||||
putClientTransaction(t)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts transaction (if h set) and writes message to server, handler
|
||||
// is called asynchronously.
|
||||
func (c *Client) Start(m *Message, h Handler) error {
|
||||
if err := c.checkInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.mux.RLock()
|
||||
closed := c.closed
|
||||
c.mux.RUnlock()
|
||||
if closed {
|
||||
return ErrClientClosed
|
||||
}
|
||||
if h != nil {
|
||||
// Starting transaction only if h is set. Useful for indications.
|
||||
t := acquireClientTransaction()
|
||||
t.id = m.TransactionID
|
||||
t.start = c.clock.Now()
|
||||
t.h = h
|
||||
t.rto = time.Duration(atomic.LoadInt64(&c.rto))
|
||||
t.attempt = 0
|
||||
t.raw = append(t.raw[:0], m.Raw...)
|
||||
t.calls = 0
|
||||
d := t.nextTimeout(t.start)
|
||||
if err := c.start(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.a.Start(m.TransactionID, d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := m.WriteTo(c.c)
|
||||
if err != nil && h != nil {
|
||||
c.delete(m.TransactionID)
|
||||
// Stopping transaction instead of waiting until deadline.
|
||||
if stopErr := c.a.Stop(m.TransactionID); stopErr != nil {
|
||||
return StopErr{
|
||||
Err: stopErr,
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
22
server/vendor/github.com/pion/stun/codecov.yml
generated
vendored
Normal file
22
server/vendor/github.com/pion/stun/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/**/*"
|
||||
162
server/vendor/github.com/pion/stun/errorcode.go
generated
vendored
Normal file
162
server/vendor/github.com/pion/stun/errorcode.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrorCodeAttribute represents ERROR-CODE attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.6
|
||||
type ErrorCodeAttribute struct {
|
||||
Code ErrorCode
|
||||
Reason []byte
|
||||
}
|
||||
|
||||
func (c ErrorCodeAttribute) String() string {
|
||||
return fmt.Sprintf("%d: %s", c.Code, c.Reason)
|
||||
}
|
||||
|
||||
// constants for ERROR-CODE encoding.
|
||||
const (
|
||||
errorCodeReasonStart = 4
|
||||
errorCodeClassByte = 2
|
||||
errorCodeNumberByte = 3
|
||||
errorCodeReasonMaxB = 763
|
||||
errorCodeModulo = 100
|
||||
)
|
||||
|
||||
// AddTo adds ERROR-CODE to m.
|
||||
func (c ErrorCodeAttribute) AddTo(m *Message) error {
|
||||
value := make([]byte, 0, errorCodeReasonStart+errorCodeReasonMaxB)
|
||||
if err := CheckOverflow(AttrErrorCode,
|
||||
len(c.Reason)+errorCodeReasonStart,
|
||||
errorCodeReasonMaxB+errorCodeReasonStart,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
value = value[:errorCodeReasonStart+len(c.Reason)]
|
||||
number := byte(c.Code % errorCodeModulo) // error code modulo 100
|
||||
class := byte(c.Code / errorCodeModulo) // hundred digit
|
||||
value[errorCodeClassByte] = class
|
||||
value[errorCodeNumberByte] = number
|
||||
copy(value[errorCodeReasonStart:], c.Reason)
|
||||
m.Add(AttrErrorCode, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFrom decodes ERROR-CODE from m. Reason is valid until m.Raw is valid.
|
||||
func (c *ErrorCodeAttribute) GetFrom(m *Message) error {
|
||||
v, err := m.Get(AttrErrorCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(v) < errorCodeReasonStart {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
var (
|
||||
class = uint16(v[errorCodeClassByte])
|
||||
number = uint16(v[errorCodeNumberByte])
|
||||
code = int(class*errorCodeModulo + number)
|
||||
)
|
||||
c.Code = ErrorCode(code)
|
||||
c.Reason = v[errorCodeReasonStart:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrorCode is code for ERROR-CODE attribute.
|
||||
type ErrorCode int
|
||||
|
||||
// ErrNoDefaultReason means that default reason for provided error code
|
||||
// is not defined in RFC.
|
||||
var ErrNoDefaultReason = errors.New("no default reason for ErrorCode")
|
||||
|
||||
// AddTo adds ERROR-CODE with default reason to m. If there
|
||||
// is no default reason, returns ErrNoDefaultReason.
|
||||
func (c ErrorCode) AddTo(m *Message) error {
|
||||
reason := errorReasons[c]
|
||||
if reason == nil {
|
||||
return ErrNoDefaultReason
|
||||
}
|
||||
a := &ErrorCodeAttribute{
|
||||
Code: c,
|
||||
Reason: reason,
|
||||
}
|
||||
return a.AddTo(m)
|
||||
}
|
||||
|
||||
// Possible error codes.
|
||||
const (
|
||||
CodeTryAlternate ErrorCode = 300
|
||||
CodeBadRequest ErrorCode = 400
|
||||
CodeUnauthorized ErrorCode = 401
|
||||
CodeUnknownAttribute ErrorCode = 420
|
||||
CodeStaleNonce ErrorCode = 438
|
||||
CodeRoleConflict ErrorCode = 487
|
||||
CodeServerError ErrorCode = 500
|
||||
)
|
||||
|
||||
// DEPRECATED constants.
|
||||
const (
|
||||
// DEPRECATED, use CodeUnauthorized.
|
||||
CodeUnauthorised = CodeUnauthorized
|
||||
)
|
||||
|
||||
// Error codes from RFC 5766.
|
||||
//
|
||||
// RFC 5766 Section 15
|
||||
const (
|
||||
CodeForbidden ErrorCode = 403 // Forbidden
|
||||
CodeAllocMismatch ErrorCode = 437 // Allocation Mismatch
|
||||
CodeWrongCredentials ErrorCode = 441 // Wrong Credentials
|
||||
CodeUnsupportedTransProto ErrorCode = 442 // Unsupported Transport Protocol
|
||||
CodeAllocQuotaReached ErrorCode = 486 // Allocation Quota Reached
|
||||
CodeInsufficientCapacity ErrorCode = 508 // Insufficient Capacity
|
||||
)
|
||||
|
||||
// Error codes from RFC 6062.
|
||||
//
|
||||
// RFC 6062 Section 6.3
|
||||
const (
|
||||
CodeConnAlreadyExists ErrorCode = 446
|
||||
CodeConnTimeoutOrFailure ErrorCode = 447
|
||||
)
|
||||
|
||||
// Error codes from RFC 6156.
|
||||
//
|
||||
// RFC 6156 Section 10.2
|
||||
const (
|
||||
CodeAddrFamilyNotSupported ErrorCode = 440 // Address Family not Supported
|
||||
CodePeerAddrFamilyMismatch ErrorCode = 443 // Peer Address Family Mismatch
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var errorReasons = map[ErrorCode][]byte{
|
||||
CodeTryAlternate: []byte("Try Alternate"),
|
||||
CodeBadRequest: []byte("Bad Request"),
|
||||
CodeUnauthorized: []byte("Unauthorized"),
|
||||
CodeUnknownAttribute: []byte("Unknown Attribute"),
|
||||
CodeStaleNonce: []byte("Stale Nonce"),
|
||||
CodeServerError: []byte("Server Error"),
|
||||
CodeRoleConflict: []byte("Role Conflict"),
|
||||
|
||||
// RFC 5766.
|
||||
CodeForbidden: []byte("Forbidden"),
|
||||
CodeAllocMismatch: []byte("Allocation Mismatch"),
|
||||
CodeWrongCredentials: []byte("Wrong Credentials"),
|
||||
CodeUnsupportedTransProto: []byte("Unsupported Transport Protocol"),
|
||||
CodeAllocQuotaReached: []byte("Allocation Quota Reached"),
|
||||
CodeInsufficientCapacity: []byte("Insufficient Capacity"),
|
||||
|
||||
// RFC 6062.
|
||||
CodeConnAlreadyExists: []byte("Connection Already Exists"),
|
||||
CodeConnTimeoutOrFailure: []byte("Connection Timeout or Failure"),
|
||||
|
||||
// RFC 6156.
|
||||
CodeAddrFamilyNotSupported: []byte("Address Family not Supported"),
|
||||
CodePeerAddrFamilyMismatch: []byte("Peer Address Family Mismatch"),
|
||||
}
|
||||
66
server/vendor/github.com/pion/stun/errors.go
generated
vendored
Normal file
66
server/vendor/github.com/pion/stun/errors.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import "errors"
|
||||
|
||||
// DecodeErr records an error and place when it is occurred.
|
||||
//
|
||||
//nolint:errname
|
||||
type DecodeErr struct {
|
||||
Place DecodeErrPlace
|
||||
Message string
|
||||
}
|
||||
|
||||
// IsInvalidCookie returns true if error means that magic cookie
|
||||
// value is invalid.
|
||||
func (e DecodeErr) IsInvalidCookie() bool {
|
||||
return e.Place == DecodeErrPlace{"message", "cookie"}
|
||||
}
|
||||
|
||||
// IsPlaceParent reports if error place parent is p.
|
||||
func (e DecodeErr) IsPlaceParent(p string) bool {
|
||||
return e.Place.Parent == p
|
||||
}
|
||||
|
||||
// IsPlaceChildren reports if error place children is c.
|
||||
func (e DecodeErr) IsPlaceChildren(c string) bool {
|
||||
return e.Place.Children == c
|
||||
}
|
||||
|
||||
// IsPlace reports if error place is p.
|
||||
func (e DecodeErr) IsPlace(p DecodeErrPlace) bool {
|
||||
return e.Place == p
|
||||
}
|
||||
|
||||
// DecodeErrPlace records a place where error is occurred.
|
||||
type DecodeErrPlace struct {
|
||||
Parent string
|
||||
Children string
|
||||
}
|
||||
|
||||
func (p DecodeErrPlace) String() string {
|
||||
return p.Parent + "/" + p.Children
|
||||
}
|
||||
|
||||
func (e DecodeErr) Error() string {
|
||||
return "BadFormat for " + e.Place.String() + ": " + e.Message
|
||||
}
|
||||
|
||||
func newDecodeErr(parent, children, message string) *DecodeErr {
|
||||
return &DecodeErr{
|
||||
Place: DecodeErrPlace{Parent: parent, Children: children},
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func newAttrDecodeErr(children, message string) *DecodeErr {
|
||||
return newDecodeErr("attribute", children, message)
|
||||
}
|
||||
|
||||
// ErrAttributeSizeInvalid means that decoded attribute size is invalid.
|
||||
var ErrAttributeSizeInvalid = errors.New("attribute size is invalid")
|
||||
|
||||
// ErrAttributeSizeOverflow means that decoded attribute size is too big.
|
||||
var ErrAttributeSizeOverflow = errors.New("attribute size overflow")
|
||||
70
server/vendor/github.com/pion/stun/fingerprint.go
generated
vendored
Normal file
70
server/vendor/github.com/pion/stun/fingerprint.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
// FingerprintAttr represents FINGERPRINT attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.5
|
||||
type FingerprintAttr struct{}
|
||||
|
||||
// ErrFingerprintMismatch means that computed fingerprint differs from expected.
|
||||
var ErrFingerprintMismatch = errors.New("fingerprint check failed")
|
||||
|
||||
// Fingerprint is shorthand for FingerprintAttr.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// m := New()
|
||||
// Fingerprint.AddTo(m)
|
||||
var Fingerprint FingerprintAttr //nolint:gochecknoglobals
|
||||
|
||||
const (
|
||||
fingerprintXORValue uint32 = 0x5354554e //nolint:staticcheck
|
||||
fingerprintSize = 4 // 32 bit
|
||||
)
|
||||
|
||||
// FingerprintValue returns CRC-32 of b XOR-ed by 0x5354554e.
|
||||
//
|
||||
// The value of the attribute is computed as the CRC-32 of the STUN message
|
||||
// up to (but excluding) the FINGERPRINT attribute itself, XOR'ed with
|
||||
// the 32-bit value 0x5354554e (the XOR helps in cases where an
|
||||
// application packet is also using CRC-32 in it).
|
||||
func FingerprintValue(b []byte) uint32 {
|
||||
return crc32.ChecksumIEEE(b) ^ fingerprintXORValue // XOR
|
||||
}
|
||||
|
||||
// AddTo adds fingerprint to message.
|
||||
func (FingerprintAttr) AddTo(m *Message) error {
|
||||
l := m.Length
|
||||
// length in header should include size of fingerprint attribute
|
||||
m.Length += fingerprintSize + attributeHeaderSize // increasing length
|
||||
m.WriteLength() // writing Length to Raw
|
||||
b := make([]byte, fingerprintSize)
|
||||
val := FingerprintValue(m.Raw)
|
||||
bin.PutUint32(b, val)
|
||||
m.Length = l
|
||||
m.Add(AttrFingerprint, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check reads fingerprint value from m and checks it, returning error if any.
|
||||
// Can return *AttrLengthErr, ErrAttributeNotFound, and *CRCMismatch.
|
||||
func (FingerprintAttr) Check(m *Message) error {
|
||||
b, err := m.Get(AttrFingerprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = CheckSize(AttrFingerprint, len(b), fingerprintSize); err != nil {
|
||||
return err
|
||||
}
|
||||
val := bin.Uint32(b)
|
||||
attrStart := len(m.Raw) - (fingerprintSize + attributeHeaderSize)
|
||||
expected := FingerprintValue(m.Raw[:attrStart])
|
||||
return checkFingerprint(val, expected)
|
||||
}
|
||||
22
server/vendor/github.com/pion/stun/fingerprint_debug.go
generated
vendored
Normal file
22
server/vendor/github.com/pion/stun/fingerprint_debug.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package stun
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CRCMismatch represents CRC check error.
|
||||
type CRCMismatch struct {
|
||||
Expected uint32
|
||||
Actual uint32
|
||||
}
|
||||
|
||||
func (m CRCMismatch) Error() string {
|
||||
return fmt.Sprintf("CRC mismatch: %x (expected) != %x (actual)",
|
||||
m.Expected,
|
||||
m.Actual,
|
||||
)
|
||||
}
|
||||
109
server/vendor/github.com/pion/stun/helpers.go
generated
vendored
Normal file
109
server/vendor/github.com/pion/stun/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
// Interfaces that are implemented by message attributes, shorthands for them,
|
||||
// or helpers for message fields as type or transaction id.
|
||||
type (
|
||||
// Setter sets *Message attribute.
|
||||
Setter interface {
|
||||
AddTo(m *Message) error
|
||||
}
|
||||
// Getter parses attribute from *Message.
|
||||
Getter interface {
|
||||
GetFrom(m *Message) error
|
||||
}
|
||||
// Checker checks *Message attribute.
|
||||
Checker interface {
|
||||
Check(m *Message) error
|
||||
}
|
||||
)
|
||||
|
||||
// Build resets message and applies setters to it in batch, returning on
|
||||
// first error. To prevent allocations, pass pointers to values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var (
|
||||
// t = BindingRequest
|
||||
// username = NewUsername("username")
|
||||
// nonce = NewNonce("nonce")
|
||||
// realm = NewRealm("example.org")
|
||||
// )
|
||||
// m := new(Message)
|
||||
// m.Build(t, username, nonce, realm) // 4 allocations
|
||||
// m.Build(&t, &username, &nonce, &realm) // 0 allocations
|
||||
//
|
||||
// See BenchmarkBuildOverhead.
|
||||
func (m *Message) Build(setters ...Setter) error {
|
||||
m.Reset()
|
||||
m.WriteHeader()
|
||||
for _, s := range setters {
|
||||
if err := s.AddTo(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check applies checkers to message in batch, returning on first error.
|
||||
func (m *Message) Check(checkers ...Checker) error {
|
||||
for _, c := range checkers {
|
||||
if err := c.Check(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse applies getters to message in batch, returning on first error.
|
||||
func (m *Message) Parse(getters ...Getter) error {
|
||||
for _, c := range getters {
|
||||
if err := c.GetFrom(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustBuild wraps Build call and panics on error.
|
||||
func MustBuild(setters ...Setter) *Message {
|
||||
m, err := Build(setters...)
|
||||
if err != nil {
|
||||
panic(err) //nolint
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Build wraps Message.Build method.
|
||||
func Build(setters ...Setter) (*Message, error) {
|
||||
m := new(Message)
|
||||
if err := m.Build(setters...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ForEach is helper that iterates over message attributes allowing to call
|
||||
// Getter in f callback to get all attributes of type t and returning on first
|
||||
// f error.
|
||||
//
|
||||
// The m.Get method inside f will be returning next attribute on each f call.
|
||||
// Does not error if there are no results.
|
||||
func (m *Message) ForEach(t AttrType, f func(m *Message) error) error {
|
||||
attrs := m.Attributes
|
||||
defer func() {
|
||||
m.Attributes = attrs
|
||||
}()
|
||||
for i, a := range attrs {
|
||||
if a.Type != t {
|
||||
continue
|
||||
}
|
||||
m.Attributes = attrs[i:]
|
||||
if err := f(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
126
server/vendor/github.com/pion/stun/integrity.go
generated
vendored
Normal file
126
server/vendor/github.com/pion/stun/integrity.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import ( //nolint:gci
|
||||
"crypto/md5" //nolint:gosec
|
||||
"crypto/sha1" //nolint:gosec
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/stun/internal/hmac"
|
||||
)
|
||||
|
||||
// separator for credentials.
|
||||
const credentialsSep = ":"
|
||||
|
||||
// NewLongTermIntegrity returns new MessageIntegrity with key for long-term
|
||||
// credentials. Password, username, and realm must be SASL-prepared.
|
||||
func NewLongTermIntegrity(username, realm, password string) MessageIntegrity {
|
||||
k := strings.Join([]string{username, realm, password}, credentialsSep)
|
||||
h := md5.New() //nolint:gosec
|
||||
fmt.Fprint(h, k)
|
||||
return MessageIntegrity(h.Sum(nil))
|
||||
}
|
||||
|
||||
// NewShortTermIntegrity returns new MessageIntegrity with key for short-term
|
||||
// credentials. Password must be SASL-prepared.
|
||||
func NewShortTermIntegrity(password string) MessageIntegrity {
|
||||
return MessageIntegrity(password)
|
||||
}
|
||||
|
||||
// MessageIntegrity represents MESSAGE-INTEGRITY attribute.
|
||||
//
|
||||
// AddTo and Check methods are using zero-allocation version of hmac, see
|
||||
// newHMAC function and internal/hmac/pool.go.
|
||||
//
|
||||
// RFC 5389 Section 15.4
|
||||
type MessageIntegrity []byte
|
||||
|
||||
func newHMAC(key, message, buf []byte) []byte {
|
||||
mac := hmac.AcquireSHA1(key)
|
||||
writeOrPanic(mac, message)
|
||||
defer hmac.PutSHA1(mac)
|
||||
return mac.Sum(buf)
|
||||
}
|
||||
|
||||
func (i MessageIntegrity) String() string {
|
||||
return fmt.Sprintf("KEY: 0x%x", []byte(i))
|
||||
}
|
||||
|
||||
const messageIntegritySize = 20
|
||||
|
||||
// ErrFingerprintBeforeIntegrity means that FINGERPRINT attribute is already in
|
||||
// message, so MESSAGE-INTEGRITY attribute cannot be added.
|
||||
var ErrFingerprintBeforeIntegrity = errors.New("FINGERPRINT before MESSAGE-INTEGRITY attribute")
|
||||
|
||||
// AddTo adds MESSAGE-INTEGRITY attribute to message.
|
||||
//
|
||||
// CPU costly, see BenchmarkMessageIntegrity_AddTo.
|
||||
func (i MessageIntegrity) AddTo(m *Message) error {
|
||||
for _, a := range m.Attributes {
|
||||
// Message should not contain FINGERPRINT attribute
|
||||
// before MESSAGE-INTEGRITY.
|
||||
if a.Type == AttrFingerprint {
|
||||
return ErrFingerprintBeforeIntegrity
|
||||
}
|
||||
}
|
||||
// The text used as input to HMAC is the STUN message,
|
||||
// including the header, up to and including the attribute preceding the
|
||||
// MESSAGE-INTEGRITY attribute.
|
||||
length := m.Length
|
||||
// Adjusting m.Length to contain MESSAGE-INTEGRITY TLV.
|
||||
m.Length += messageIntegritySize + attributeHeaderSize
|
||||
m.WriteLength() // writing length to m.Raw
|
||||
v := newHMAC(i, m.Raw, m.Raw[len(m.Raw):]) // calculating HMAC for adjusted m.Raw
|
||||
m.Length = length // changing m.Length back
|
||||
|
||||
// Copy hmac value to temporary variable to protect it from resetting
|
||||
// while processing m.Add call.
|
||||
vBuf := make([]byte, sha1.Size)
|
||||
copy(vBuf, v)
|
||||
|
||||
m.Add(AttrMessageIntegrity, vBuf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrIntegrityMismatch means that computed HMAC differs from expected.
|
||||
var ErrIntegrityMismatch = errors.New("integrity check failed")
|
||||
|
||||
// Check checks MESSAGE-INTEGRITY attribute.
|
||||
//
|
||||
// CPU costly, see BenchmarkMessageIntegrity_Check.
|
||||
func (i MessageIntegrity) Check(m *Message) error {
|
||||
v, err := m.Get(AttrMessageIntegrity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Adjusting length in header to match m.Raw that was
|
||||
// used when computing HMAC.
|
||||
var (
|
||||
length = m.Length
|
||||
afterIntegrity = false
|
||||
sizeReduced int
|
||||
)
|
||||
for _, a := range m.Attributes {
|
||||
if afterIntegrity {
|
||||
sizeReduced += nearestPaddedValueLength(int(a.Length))
|
||||
sizeReduced += attributeHeaderSize
|
||||
}
|
||||
if a.Type == AttrMessageIntegrity {
|
||||
afterIntegrity = true
|
||||
}
|
||||
}
|
||||
m.Length -= uint32(sizeReduced)
|
||||
m.WriteLength()
|
||||
// startOfHMAC should be first byte of integrity attribute.
|
||||
startOfHMAC := messageHeaderSize + m.Length - (attributeHeaderSize + messageIntegritySize)
|
||||
b := m.Raw[:startOfHMAC] // data before integrity attribute
|
||||
expected := newHMAC(i, b, m.Raw[len(m.Raw):])
|
||||
m.Length = length
|
||||
m.WriteLength() // writing length back
|
||||
return checkHMAC(v, expected)
|
||||
}
|
||||
22
server/vendor/github.com/pion/stun/integrity_debug.go
generated
vendored
Normal file
22
server/vendor/github.com/pion/stun/integrity_debug.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package stun
|
||||
|
||||
import "fmt"
|
||||
|
||||
// IntegrityErr occurs when computed HMAC differs from expected.
|
||||
type IntegrityErr struct {
|
||||
Expected []byte
|
||||
Actual []byte
|
||||
}
|
||||
|
||||
func (i *IntegrityErr) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Integrity check failed: 0x%x (expected) !- 0x%x (actual)",
|
||||
i.Expected, i.Actual,
|
||||
)
|
||||
}
|
||||
156
server/vendor/github.com/pion/stun/internal/hmac/hmac.go
generated
vendored
Normal file
156
server/vendor/github.com/pion/stun/internal/hmac/hmac.go
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
// SPDX-FileCopyrightText: 2009 The Go Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
/*
|
||||
Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
|
||||
defined in U.S. Federal Information Processing Standards Publication 198.
|
||||
An HMAC is a cryptographic hash that uses a key to sign a message.
|
||||
The receiver verifies the hash by recomputing it using the same key.
|
||||
|
||||
Receivers should be careful to use Equal to compare MACs in order to avoid
|
||||
timing side-channels:
|
||||
|
||||
// ValidMAC reports whether messageMAC is a valid HMAC tag for message.
|
||||
func ValidMAC(message, messageMAC, key []byte) bool {
|
||||
mac := hmac.New(sha256.New, key)
|
||||
mac.Write(message)
|
||||
expectedMAC := mac.Sum(nil)
|
||||
return hmac.Equal(messageMAC, expectedMAC)
|
||||
}
|
||||
*/
|
||||
package hmac
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// FIPS 198-1:
|
||||
// https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf
|
||||
|
||||
// key is zero padded to the block size of the hash function
|
||||
// ipad = 0x36 byte repeated for key length
|
||||
// opad = 0x5c byte repeated for key length
|
||||
// hmac = H([key ^ opad] H([key ^ ipad] text))
|
||||
|
||||
// Marshalable is the combination of encoding.BinaryMarshaler and
|
||||
// encoding.BinaryUnmarshaler. Their method definitions are repeated here to
|
||||
// avoid a dependency on the encoding package.
|
||||
type marshalable interface {
|
||||
MarshalBinary() ([]byte, error)
|
||||
UnmarshalBinary([]byte) error
|
||||
}
|
||||
|
||||
type hmac struct {
|
||||
opad, ipad []byte
|
||||
outer, inner hash.Hash
|
||||
|
||||
// If marshaled is true, then opad and ipad do not contain a padded
|
||||
// copy of the key, but rather the marshaled state of outer/inner after
|
||||
// opad/ipad has been fed into it.
|
||||
marshaled bool
|
||||
}
|
||||
|
||||
func (h *hmac) Sum(in []byte) []byte {
|
||||
origLen := len(in)
|
||||
in = h.inner.Sum(in)
|
||||
|
||||
if h.marshaled {
|
||||
if err := h.outer.(marshalable).UnmarshalBinary(h.opad); err != nil { //nolint:forcetypeassert
|
||||
panic(err) //nolint
|
||||
}
|
||||
} else {
|
||||
h.outer.Reset()
|
||||
h.outer.Write(h.opad) //nolint:errcheck,gosec
|
||||
}
|
||||
h.outer.Write(in[origLen:]) //nolint:errcheck,gosec
|
||||
return h.outer.Sum(in[:origLen])
|
||||
}
|
||||
|
||||
func (h *hmac) Write(p []byte) (n int, err error) {
|
||||
return h.inner.Write(p)
|
||||
}
|
||||
|
||||
func (h *hmac) Size() int { return h.outer.Size() }
|
||||
func (h *hmac) BlockSize() int { return h.inner.BlockSize() }
|
||||
|
||||
func (h *hmac) Reset() {
|
||||
if h.marshaled {
|
||||
if err := h.inner.(marshalable).UnmarshalBinary(h.ipad); err != nil { //nolint:forcetypeassert
|
||||
panic(err) //nolint
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h.inner.Reset()
|
||||
h.inner.Write(h.ipad) //nolint:errcheck,gosec
|
||||
|
||||
// If the underlying hash is marshalable, we can save some time by
|
||||
// saving a copy of the hash state now, and restoring it on future
|
||||
// calls to Reset and Sum instead of writing ipad/opad every time.
|
||||
//
|
||||
// If either hash is unmarshalable for whatever reason,
|
||||
// it's safe to bail out here.
|
||||
marshalableInner, innerOK := h.inner.(marshalable)
|
||||
if !innerOK {
|
||||
return
|
||||
}
|
||||
marshalableOuter, outerOK := h.outer.(marshalable)
|
||||
if !outerOK {
|
||||
return
|
||||
}
|
||||
|
||||
imarshal, err := marshalableInner.MarshalBinary()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h.outer.Reset()
|
||||
h.outer.Write(h.opad) //nolint:errcheck,gosec
|
||||
omarshal, err := marshalableOuter.MarshalBinary()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Marshaling succeeded; save the marshaled state for later
|
||||
h.ipad = imarshal
|
||||
h.opad = omarshal
|
||||
h.marshaled = true
|
||||
}
|
||||
|
||||
// New returns a new HMAC hash using the given hash.Hash type and key.
|
||||
// Note that unlike other hash implementations in the standard library,
|
||||
// the returned Hash does not implement encoding.BinaryMarshaler
|
||||
// or encoding.BinaryUnmarshaler.
|
||||
func New(h func() hash.Hash, key []byte) hash.Hash {
|
||||
hm := new(hmac)
|
||||
hm.outer = h()
|
||||
hm.inner = h()
|
||||
blocksize := hm.inner.BlockSize()
|
||||
hm.ipad = make([]byte, blocksize)
|
||||
hm.opad = make([]byte, blocksize)
|
||||
if len(key) > blocksize {
|
||||
// If key is too big, hash it.
|
||||
hm.outer.Write(key) //nolint:errcheck,gosec
|
||||
key = hm.outer.Sum(nil)
|
||||
}
|
||||
copy(hm.ipad, key)
|
||||
copy(hm.opad, key)
|
||||
for i := range hm.ipad {
|
||||
hm.ipad[i] ^= 0x36
|
||||
}
|
||||
for i := range hm.opad {
|
||||
hm.opad[i] ^= 0x5c
|
||||
}
|
||||
hm.inner.Write(hm.ipad) //nolint:errcheck,gosec
|
||||
|
||||
return hm
|
||||
}
|
||||
|
||||
// Equal compares two MACs for equality without leaking timing information.
|
||||
func Equal(mac1, mac2 []byte) bool {
|
||||
// We don't have to be constant time if the lengths of the MACs are
|
||||
// different as that suggests that a completely different hash function
|
||||
// was used.
|
||||
return subtle.ConstantTimeCompare(mac1, mac2) == 1
|
||||
}
|
||||
92
server/vendor/github.com/pion/stun/internal/hmac/pool.go
generated
vendored
Normal file
92
server/vendor/github.com/pion/stun/internal/hmac/pool.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hmac
|
||||
|
||||
import ( //nolint:gci
|
||||
"crypto/sha1" //nolint:gosec
|
||||
"crypto/sha256"
|
||||
"hash"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (h *hmac) resetTo(key []byte) {
|
||||
h.outer.Reset()
|
||||
h.inner.Reset()
|
||||
blocksize := h.inner.BlockSize()
|
||||
|
||||
// Reset size and zero of ipad and opad.
|
||||
h.ipad = append(h.ipad[:0], make([]byte, blocksize)...)
|
||||
h.opad = append(h.opad[:0], make([]byte, blocksize)...)
|
||||
|
||||
if len(key) > blocksize {
|
||||
// If key is too big, hash it.
|
||||
h.outer.Write(key) //nolint:errcheck,gosec
|
||||
key = h.outer.Sum(nil)
|
||||
}
|
||||
copy(h.ipad, key)
|
||||
copy(h.opad, key)
|
||||
for i := range h.ipad {
|
||||
h.ipad[i] ^= 0x36
|
||||
}
|
||||
for i := range h.opad {
|
||||
h.opad[i] ^= 0x5c
|
||||
}
|
||||
h.inner.Write(h.ipad) //nolint:errcheck,gosec
|
||||
|
||||
h.marshaled = false
|
||||
}
|
||||
|
||||
var hmacSHA1Pool = &sync.Pool{ //nolint:gochecknoglobals
|
||||
New: func() interface{} {
|
||||
h := New(sha1.New, make([]byte, sha1.BlockSize))
|
||||
return h
|
||||
},
|
||||
}
|
||||
|
||||
// AcquireSHA1 returns new HMAC from pool.
|
||||
func AcquireSHA1(key []byte) hash.Hash {
|
||||
h := hmacSHA1Pool.Get().(*hmac) //nolint:forcetypeassert
|
||||
assertHMACSize(h, sha1.Size, sha1.BlockSize)
|
||||
h.resetTo(key)
|
||||
return h
|
||||
}
|
||||
|
||||
// PutSHA1 puts h to pool.
|
||||
func PutSHA1(h hash.Hash) {
|
||||
hm := h.(*hmac) //nolint:forcetypeassert
|
||||
assertHMACSize(hm, sha1.Size, sha1.BlockSize)
|
||||
hmacSHA1Pool.Put(hm)
|
||||
}
|
||||
|
||||
var hmacSHA256Pool = &sync.Pool{ //nolint:gochecknoglobals
|
||||
New: func() interface{} {
|
||||
h := New(sha256.New, make([]byte, sha256.BlockSize))
|
||||
return h
|
||||
},
|
||||
}
|
||||
|
||||
// AcquireSHA256 returns new HMAC from SHA256 pool.
|
||||
func AcquireSHA256(key []byte) hash.Hash {
|
||||
h := hmacSHA256Pool.Get().(*hmac) //nolint:forcetypeassert
|
||||
assertHMACSize(h, sha256.Size, sha256.BlockSize)
|
||||
h.resetTo(key)
|
||||
return h
|
||||
}
|
||||
|
||||
// PutSHA256 puts h to SHA256 pool.
|
||||
func PutSHA256(h hash.Hash) {
|
||||
hm := h.(*hmac) //nolint:forcetypeassert
|
||||
assertHMACSize(hm, sha256.Size, sha256.BlockSize)
|
||||
hmacSHA256Pool.Put(hm)
|
||||
}
|
||||
|
||||
// assertHMACSize panics if h.size != size or h.blocksize != blocksize.
|
||||
//
|
||||
// Put and Acquire functions are internal functions to project, so
|
||||
// checking it via such assert is optimal.
|
||||
func assertHMACSize(h *hmac, size, blocksize int) { //nolint:unparam
|
||||
if h.Size() != size || h.BlockSize() != blocksize {
|
||||
panic("BUG: hmac size invalid") //nolint
|
||||
}
|
||||
}
|
||||
7
server/vendor/github.com/pion/stun/internal/hmac/vendor.sh
generated
vendored
Normal file
7
server/vendor/github.com/pion/stun/internal/hmac/vendor.sh
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/env bash
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cp -v $GOROOT/src/crypto/hmac/{hmac,hmac_test}.go .
|
||||
git diff {hmac,hmac_test}.go
|
||||
622
server/vendor/github.com/pion/stun/message.go
generated
vendored
Normal file
622
server/vendor/github.com/pion/stun/message.go
generated
vendored
Normal file
@@ -0,0 +1,622 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// magicCookie is fixed value that aids in distinguishing STUN packets
|
||||
// from packets of other protocols when STUN is multiplexed with those
|
||||
// other protocols on the same Port.
|
||||
//
|
||||
// The magic cookie field MUST contain the fixed value 0x2112A442 in
|
||||
// network byte order.
|
||||
//
|
||||
// Defined in "STUN Message Structure", section 6.
|
||||
magicCookie = 0x2112A442
|
||||
attributeHeaderSize = 4
|
||||
messageHeaderSize = 20
|
||||
|
||||
// TransactionIDSize is length of transaction id array (in bytes).
|
||||
TransactionIDSize = 12 // 96 bit
|
||||
)
|
||||
|
||||
// NewTransactionID returns new random transaction ID using crypto/rand
|
||||
// as source.
|
||||
func NewTransactionID() (b [TransactionIDSize]byte) {
|
||||
readFullOrPanic(rand.Reader, b[:])
|
||||
return b
|
||||
}
|
||||
|
||||
// IsMessage returns true if b looks like STUN message.
|
||||
// Useful for multiplexing. IsMessage does not guarantee
|
||||
// that decoding will be successful.
|
||||
func IsMessage(b []byte) bool {
|
||||
return len(b) >= messageHeaderSize && bin.Uint32(b[4:8]) == magicCookie
|
||||
}
|
||||
|
||||
// New returns *Message with pre-allocated Raw.
|
||||
func New() *Message {
|
||||
const defaultRawCapacity = 120
|
||||
return &Message{
|
||||
Raw: make([]byte, messageHeaderSize, defaultRawCapacity),
|
||||
}
|
||||
}
|
||||
|
||||
// ErrDecodeToNil occurs on Decode(data, nil) call.
|
||||
var ErrDecodeToNil = errors.New("attempt to decode to nil message")
|
||||
|
||||
// Decode decodes Message from data to m, returning error if any.
|
||||
func Decode(data []byte, m *Message) error {
|
||||
if m == nil {
|
||||
return ErrDecodeToNil
|
||||
}
|
||||
m.Raw = append(m.Raw[:0], data...)
|
||||
return m.Decode()
|
||||
}
|
||||
|
||||
// Message represents a single STUN packet. It uses aggressive internal
|
||||
// buffering to enable zero-allocation encoding and decoding,
|
||||
// so there are some usage constraints:
|
||||
//
|
||||
// Message, its fields, results of m.Get or any attribute a.GetFrom
|
||||
// are valid only until Message.Raw is not modified.
|
||||
type Message struct {
|
||||
Type MessageType
|
||||
Length uint32 // len(Raw) not including header
|
||||
TransactionID [TransactionIDSize]byte
|
||||
Attributes Attributes
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||
func (m Message) MarshalBinary() (data []byte, err error) {
|
||||
// We can't return m.Raw, allocation is expected by implicit interface
|
||||
// contract induced by other implementations.
|
||||
b := make([]byte, len(m.Raw))
|
||||
copy(b, m.Raw)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
||||
func (m *Message) UnmarshalBinary(data []byte) error {
|
||||
// We can't retain data, copy is expected by interface contract.
|
||||
m.Raw = append(m.Raw[:0], data...)
|
||||
return m.Decode()
|
||||
}
|
||||
|
||||
// GobEncode implements the gob.GobEncoder interface.
|
||||
func (m Message) GobEncode() ([]byte, error) {
|
||||
return m.MarshalBinary()
|
||||
}
|
||||
|
||||
// GobDecode implements the gob.GobDecoder interface.
|
||||
func (m *Message) GobDecode(data []byte) error {
|
||||
return m.UnmarshalBinary(data)
|
||||
}
|
||||
|
||||
// AddTo sets b.TransactionID to m.TransactionID.
|
||||
//
|
||||
// Implements Setter to aid in crafting responses.
|
||||
func (m *Message) AddTo(b *Message) error {
|
||||
b.TransactionID = m.TransactionID
|
||||
b.WriteTransactionID()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTransactionID sets m.TransactionID to random value from crypto/rand
|
||||
// and returns error if any.
|
||||
func (m *Message) NewTransactionID() error {
|
||||
_, err := io.ReadFull(rand.Reader, m.TransactionID[:])
|
||||
if err == nil {
|
||||
m.WriteTransactionID()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Message) String() string {
|
||||
tID := base64.StdEncoding.EncodeToString(m.TransactionID[:])
|
||||
aInfo := ""
|
||||
for k, a := range m.Attributes {
|
||||
aInfo += fmt.Sprintf("attr%d=%s ", k, a.Type)
|
||||
}
|
||||
return fmt.Sprintf("%s l=%d attrs=%d id=%s, %s", m.Type, m.Length, len(m.Attributes), tID, aInfo)
|
||||
}
|
||||
|
||||
// Reset resets Message, attributes and underlying buffer length.
|
||||
func (m *Message) Reset() {
|
||||
m.Raw = m.Raw[:0]
|
||||
m.Length = 0
|
||||
m.Attributes = m.Attributes[:0]
|
||||
}
|
||||
|
||||
// grow ensures that internal buffer has n length.
|
||||
func (m *Message) grow(n int) {
|
||||
if len(m.Raw) >= n {
|
||||
return
|
||||
}
|
||||
if cap(m.Raw) >= n {
|
||||
m.Raw = m.Raw[:n]
|
||||
return
|
||||
}
|
||||
m.Raw = append(m.Raw, make([]byte, n-len(m.Raw))...)
|
||||
}
|
||||
|
||||
// Add appends new attribute to message. Not goroutine-safe.
|
||||
//
|
||||
// Value of attribute is copied to internal buffer so
|
||||
// it is safe to reuse v.
|
||||
func (m *Message) Add(t AttrType, v []byte) {
|
||||
// Allocating buffer for TLV (type-length-value).
|
||||
// T = t, L = len(v), V = v.
|
||||
// m.Raw will look like:
|
||||
// [0:20] <- message header
|
||||
// [20:20+m.Length] <- existing message attributes
|
||||
// [20+m.Length:20+m.Length+len(v) + 4] <- allocated buffer for new TLV
|
||||
// [first:last] <- same as previous
|
||||
// [0 1|2 3|4 4 + len(v)] <- mapping for allocated buffer
|
||||
// T L V
|
||||
allocSize := attributeHeaderSize + len(v) // ~ len(TLV) = len(TL) + len(V)
|
||||
first := messageHeaderSize + int(m.Length) // first byte number
|
||||
last := first + allocSize // last byte number
|
||||
m.grow(last) // growing cap(Raw) to fit TLV
|
||||
m.Raw = m.Raw[:last] // now len(Raw) = last
|
||||
m.Length += uint32(allocSize) // rendering length change
|
||||
|
||||
// Sub-slicing internal buffer to simplify encoding.
|
||||
buf := m.Raw[first:last] // slice for TLV
|
||||
value := buf[attributeHeaderSize:] // slice for V
|
||||
attr := RawAttribute{
|
||||
Type: t, // T
|
||||
Length: uint16(len(v)), // L
|
||||
Value: value, // V
|
||||
}
|
||||
|
||||
// Encoding attribute TLV to allocated buffer.
|
||||
bin.PutUint16(buf[0:2], attr.Type.Value()) // T
|
||||
bin.PutUint16(buf[2:4], attr.Length) // L
|
||||
copy(value, v) // V
|
||||
|
||||
// Checking that attribute value needs padding.
|
||||
if attr.Length%padding != 0 {
|
||||
// Performing padding.
|
||||
bytesToAdd := nearestPaddedValueLength(len(v)) - len(v)
|
||||
last += bytesToAdd
|
||||
m.grow(last)
|
||||
// setting all padding bytes to zero
|
||||
// to prevent data leak from previous
|
||||
// data in next bytesToAdd bytes
|
||||
buf = m.Raw[last-bytesToAdd : last]
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
m.Raw = m.Raw[:last] // increasing buffer length
|
||||
m.Length += uint32(bytesToAdd) // rendering length change
|
||||
}
|
||||
m.Attributes = append(m.Attributes, attr)
|
||||
m.WriteLength()
|
||||
}
|
||||
|
||||
func attrSliceEqual(a, b Attributes) bool {
|
||||
for _, attr := range a {
|
||||
found := false
|
||||
for _, attrB := range b {
|
||||
if attrB.Type != attr.Type {
|
||||
continue
|
||||
}
|
||||
if attrB.Equal(attr) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func attrEqual(a, b Attributes) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
if !attrSliceEqual(a, b) {
|
||||
return false
|
||||
}
|
||||
if !attrSliceEqual(b, a) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if Message b equals to m.
|
||||
// Ignores m.Raw.
|
||||
func (m *Message) Equal(b *Message) bool {
|
||||
if m == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
if m == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
if m.Type != b.Type {
|
||||
return false
|
||||
}
|
||||
if m.TransactionID != b.TransactionID {
|
||||
return false
|
||||
}
|
||||
if m.Length != b.Length {
|
||||
return false
|
||||
}
|
||||
if !attrEqual(m.Attributes, b.Attributes) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// WriteLength writes m.Length to m.Raw.
|
||||
func (m *Message) WriteLength() {
|
||||
m.grow(4)
|
||||
bin.PutUint16(m.Raw[2:4], uint16(m.Length))
|
||||
}
|
||||
|
||||
// WriteHeader writes header to underlying buffer. Not goroutine-safe.
|
||||
func (m *Message) WriteHeader() {
|
||||
m.grow(messageHeaderSize)
|
||||
_ = m.Raw[:messageHeaderSize] // early bounds check to guarantee safety of writes below
|
||||
|
||||
m.WriteType()
|
||||
m.WriteLength()
|
||||
bin.PutUint32(m.Raw[4:8], magicCookie) // magic cookie
|
||||
copy(m.Raw[8:messageHeaderSize], m.TransactionID[:]) // transaction ID
|
||||
}
|
||||
|
||||
// WriteTransactionID writes m.TransactionID to m.Raw.
|
||||
func (m *Message) WriteTransactionID() {
|
||||
copy(m.Raw[8:messageHeaderSize], m.TransactionID[:]) // transaction ID
|
||||
}
|
||||
|
||||
// WriteAttributes encodes all m.Attributes to m.
|
||||
func (m *Message) WriteAttributes() {
|
||||
attributes := m.Attributes
|
||||
m.Attributes = attributes[:0]
|
||||
for _, a := range attributes {
|
||||
m.Add(a.Type, a.Value)
|
||||
}
|
||||
m.Attributes = attributes
|
||||
}
|
||||
|
||||
// WriteType writes m.Type to m.Raw.
|
||||
func (m *Message) WriteType() {
|
||||
m.grow(2)
|
||||
bin.PutUint16(m.Raw[0:2], m.Type.Value()) // message type
|
||||
}
|
||||
|
||||
// SetType sets m.Type and writes it to m.Raw.
|
||||
func (m *Message) SetType(t MessageType) {
|
||||
m.Type = t
|
||||
m.WriteType()
|
||||
}
|
||||
|
||||
// Encode re-encodes message into m.Raw.
|
||||
func (m *Message) Encode() {
|
||||
m.Raw = m.Raw[:0]
|
||||
m.WriteHeader()
|
||||
m.Length = 0
|
||||
m.WriteAttributes()
|
||||
}
|
||||
|
||||
// WriteTo implements WriterTo via calling Write(m.Raw) on w and returning
|
||||
// call result.
|
||||
func (m *Message) WriteTo(w io.Writer) (int64, error) {
|
||||
n, err := w.Write(m.Raw)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// ReadFrom implements ReaderFrom. Reads message from r into m.Raw,
|
||||
// Decodes it and return error if any. If m.Raw is too small, will return
|
||||
// ErrUnexpectedEOF, ErrUnexpectedHeaderEOF or *DecodeErr.
|
||||
//
|
||||
// Can return *DecodeErr while decoding too.
|
||||
func (m *Message) ReadFrom(r io.Reader) (int64, error) {
|
||||
tBuf := m.Raw[:cap(m.Raw)]
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
if n, err = r.Read(tBuf); err != nil {
|
||||
return int64(n), err
|
||||
}
|
||||
m.Raw = tBuf[:n]
|
||||
return int64(n), m.Decode()
|
||||
}
|
||||
|
||||
// ErrUnexpectedHeaderEOF means that there were not enough bytes in
|
||||
// m.Raw to read header.
|
||||
var ErrUnexpectedHeaderEOF = errors.New("unexpected EOF: not enough bytes to read header")
|
||||
|
||||
// Decode decodes m.Raw into m.
|
||||
func (m *Message) Decode() error {
|
||||
// decoding message header
|
||||
buf := m.Raw
|
||||
if len(buf) < messageHeaderSize {
|
||||
return ErrUnexpectedHeaderEOF
|
||||
}
|
||||
var (
|
||||
t = bin.Uint16(buf[0:2]) // first 2 bytes
|
||||
size = int(bin.Uint16(buf[2:4])) // second 2 bytes
|
||||
cookie = bin.Uint32(buf[4:8]) // last 4 bytes
|
||||
fullSize = messageHeaderSize + size // len(m.Raw)
|
||||
)
|
||||
if cookie != magicCookie {
|
||||
msg := fmt.Sprintf("%x is invalid magic cookie (should be %x)", cookie, magicCookie)
|
||||
return newDecodeErr("message", "cookie", msg)
|
||||
}
|
||||
if len(buf) < fullSize {
|
||||
msg := fmt.Sprintf("buffer length %d is less than %d (expected message size)", len(buf), fullSize)
|
||||
return newAttrDecodeErr("message", msg)
|
||||
}
|
||||
// saving header data
|
||||
m.Type.ReadValue(t)
|
||||
m.Length = uint32(size)
|
||||
copy(m.TransactionID[:], buf[8:messageHeaderSize])
|
||||
|
||||
m.Attributes = m.Attributes[:0]
|
||||
var (
|
||||
offset = 0
|
||||
b = buf[messageHeaderSize:fullSize]
|
||||
)
|
||||
for offset < size {
|
||||
// checking that we have enough bytes to read header
|
||||
if len(b) < attributeHeaderSize {
|
||||
msg := fmt.Sprintf("buffer length %d is less than %d (expected header size)", len(b), attributeHeaderSize)
|
||||
return newAttrDecodeErr("header", msg)
|
||||
}
|
||||
var (
|
||||
a = RawAttribute{
|
||||
Type: compatAttrType(bin.Uint16(b[0:2])), // first 2 bytes
|
||||
Length: bin.Uint16(b[2:4]), // second 2 bytes
|
||||
}
|
||||
aL = int(a.Length) // attribute length
|
||||
aBuffL = nearestPaddedValueLength(aL) // expected buffer length (with padding)
|
||||
)
|
||||
b = b[attributeHeaderSize:] // slicing again to simplify value read
|
||||
offset += attributeHeaderSize
|
||||
if len(b) < aBuffL { // checking size
|
||||
msg := fmt.Sprintf("buffer length %d is less than %d (expected value size for %s)", len(b), aBuffL, a.Type)
|
||||
return newAttrDecodeErr("value", msg)
|
||||
}
|
||||
a.Value = b[:aL]
|
||||
offset += aBuffL
|
||||
b = b[aBuffL:]
|
||||
|
||||
m.Attributes = append(m.Attributes, a)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write decodes message and return error if any.
|
||||
//
|
||||
// Any error is unrecoverable, but message could be partially decoded.
|
||||
func (m *Message) Write(tBuf []byte) (int, error) {
|
||||
m.Raw = append(m.Raw[:0], tBuf...)
|
||||
return len(tBuf), m.Decode()
|
||||
}
|
||||
|
||||
// CloneTo clones m to b securing any further m mutations.
|
||||
func (m *Message) CloneTo(b *Message) error {
|
||||
b.Raw = append(b.Raw[:0], m.Raw...)
|
||||
return b.Decode()
|
||||
}
|
||||
|
||||
// MessageClass is 8-bit representation of 2-bit class of STUN Message Class.
|
||||
type MessageClass byte
|
||||
|
||||
// Possible values for message class in STUN Message Type.
|
||||
const (
|
||||
ClassRequest MessageClass = 0x00 // 0b00
|
||||
ClassIndication MessageClass = 0x01 // 0b01
|
||||
ClassSuccessResponse MessageClass = 0x02 // 0b10
|
||||
ClassErrorResponse MessageClass = 0x03 // 0b11
|
||||
)
|
||||
|
||||
// Common STUN message types.
|
||||
var (
|
||||
// Binding request message type.
|
||||
BindingRequest = NewType(MethodBinding, ClassRequest) //nolint:gochecknoglobals
|
||||
// Binding success response message type
|
||||
BindingSuccess = NewType(MethodBinding, ClassSuccessResponse) //nolint:gochecknoglobals
|
||||
// Binding error response message type.
|
||||
BindingError = NewType(MethodBinding, ClassErrorResponse) //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
func (c MessageClass) String() string {
|
||||
switch c {
|
||||
case ClassRequest:
|
||||
return "request"
|
||||
case ClassIndication:
|
||||
return "indication"
|
||||
case ClassSuccessResponse:
|
||||
return "success response"
|
||||
case ClassErrorResponse:
|
||||
return "error response"
|
||||
default:
|
||||
panic("unknown message class") //nolint
|
||||
}
|
||||
}
|
||||
|
||||
// Method is uint16 representation of 12-bit STUN method.
|
||||
type Method uint16
|
||||
|
||||
// Possible methods for STUN Message.
|
||||
const (
|
||||
MethodBinding Method = 0x001
|
||||
MethodAllocate Method = 0x003
|
||||
MethodRefresh Method = 0x004
|
||||
MethodSend Method = 0x006
|
||||
MethodData Method = 0x007
|
||||
MethodCreatePermission Method = 0x008
|
||||
MethodChannelBind Method = 0x009
|
||||
)
|
||||
|
||||
// Methods from RFC 6062.
|
||||
const (
|
||||
MethodConnect Method = 0x000a
|
||||
MethodConnectionBind Method = 0x000b
|
||||
MethodConnectionAttempt Method = 0x000c
|
||||
)
|
||||
|
||||
func methodName() map[Method]string {
|
||||
return map[Method]string{
|
||||
MethodBinding: "Binding",
|
||||
MethodAllocate: "Allocate",
|
||||
MethodRefresh: "Refresh",
|
||||
MethodSend: "Send",
|
||||
MethodData: "Data",
|
||||
MethodCreatePermission: "CreatePermission",
|
||||
MethodChannelBind: "ChannelBind",
|
||||
|
||||
// RFC 6062.
|
||||
MethodConnect: "Connect",
|
||||
MethodConnectionBind: "ConnectionBind",
|
||||
MethodConnectionAttempt: "ConnectionAttempt",
|
||||
}
|
||||
}
|
||||
|
||||
func (m Method) String() string {
|
||||
s, ok := methodName()[m]
|
||||
if !ok {
|
||||
// Falling back to hex representation.
|
||||
s = fmt.Sprintf("0x%x", uint16(m))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// MessageType is STUN Message Type Field.
|
||||
type MessageType struct {
|
||||
Method Method // e.g. binding
|
||||
Class MessageClass // e.g. request
|
||||
}
|
||||
|
||||
// AddTo sets m type to t.
|
||||
func (t MessageType) AddTo(m *Message) error {
|
||||
m.SetType(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewType returns new message type with provided method and class.
|
||||
func NewType(method Method, class MessageClass) MessageType {
|
||||
return MessageType{
|
||||
Method: method,
|
||||
Class: class,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
methodABits = 0xf // 0b0000000000001111
|
||||
methodBBits = 0x70 // 0b0000000001110000
|
||||
methodDBits = 0xf80 // 0b0000111110000000
|
||||
|
||||
methodBShift = 1
|
||||
methodDShift = 2
|
||||
|
||||
firstBit = 0x1
|
||||
secondBit = 0x2
|
||||
|
||||
c0Bit = firstBit
|
||||
c1Bit = secondBit
|
||||
|
||||
classC0Shift = 4
|
||||
classC1Shift = 7
|
||||
)
|
||||
|
||||
// Value returns bit representation of messageType.
|
||||
func (t MessageType) Value() uint16 {
|
||||
// 0 1
|
||||
// 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |M |M |M|M|M|C|M|M|M|C|M|M|M|M|
|
||||
// |11|10|9|8|7|1|6|5|4|0|3|2|1|0|
|
||||
// +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// Figure 3: Format of STUN Message Type Field
|
||||
|
||||
// Warning: Abandon all hope ye who enter here.
|
||||
// Splitting M into A(M0-M3), B(M4-M6), D(M7-M11).
|
||||
m := uint16(t.Method)
|
||||
a := m & methodABits // A = M * 0b0000000000001111 (right 4 bits)
|
||||
b := m & methodBBits // B = M * 0b0000000001110000 (3 bits after A)
|
||||
d := m & methodDBits // D = M * 0b0000111110000000 (5 bits after B)
|
||||
|
||||
// Shifting to add "holes" for C0 (at 4 bit) and C1 (8 bit).
|
||||
m = a + (b << methodBShift) + (d << methodDShift)
|
||||
|
||||
// C0 is zero bit of C, C1 is first bit.
|
||||
// C0 = C * 0b01, C1 = (C * 0b10) >> 1
|
||||
// Ct = C0 << 4 + C1 << 8.
|
||||
// Optimizations: "((C * 0b10) >> 1) << 8" as "(C * 0b10) << 7"
|
||||
// We need C0 shifted by 4, and C1 by 8 to fit "11" and "7" positions
|
||||
// (see figure 3).
|
||||
c := uint16(t.Class)
|
||||
c0 := (c & c0Bit) << classC0Shift
|
||||
c1 := (c & c1Bit) << classC1Shift
|
||||
class := c0 + c1
|
||||
|
||||
return m + class
|
||||
}
|
||||
|
||||
// ReadValue decodes uint16 into MessageType.
|
||||
func (t *MessageType) ReadValue(v uint16) {
|
||||
// Decoding class.
|
||||
// We are taking first bit from v >> 4 and second from v >> 7.
|
||||
c0 := (v >> classC0Shift) & c0Bit
|
||||
c1 := (v >> classC1Shift) & c1Bit
|
||||
class := c0 + c1
|
||||
t.Class = MessageClass(class)
|
||||
|
||||
// Decoding method.
|
||||
a := v & methodABits // A(M0-M3)
|
||||
b := (v >> methodBShift) & methodBBits // B(M4-M6)
|
||||
d := (v >> methodDShift) & methodDBits // D(M7-M11)
|
||||
m := a + b + d
|
||||
t.Method = Method(m)
|
||||
}
|
||||
|
||||
func (t MessageType) String() string {
|
||||
return fmt.Sprintf("%s %s", t.Method, t.Class)
|
||||
}
|
||||
|
||||
// Contains return true if message contain t attribute.
|
||||
func (m *Message) Contains(t AttrType) bool {
|
||||
for _, a := range m.Attributes {
|
||||
if a.Type == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type transactionIDValueSetter [TransactionIDSize]byte
|
||||
|
||||
// NewTransactionIDSetter returns new Setter that sets message transaction id
|
||||
// to provided value.
|
||||
func NewTransactionIDSetter(value [TransactionIDSize]byte) Setter {
|
||||
return transactionIDValueSetter(value)
|
||||
}
|
||||
|
||||
func (t transactionIDValueSetter) AddTo(m *Message) error {
|
||||
m.TransactionID = t
|
||||
m.WriteTransactionID()
|
||||
return nil
|
||||
}
|
||||
6
server/vendor/github.com/pion/stun/renovate.json
generated
vendored
Normal file
6
server/vendor/github.com/pion/stun/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>pion/renovate-config"
|
||||
]
|
||||
}
|
||||
54
server/vendor/github.com/pion/stun/stun.go
generated
vendored
Normal file
54
server/vendor/github.com/pion/stun/stun.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389.
|
||||
//
|
||||
// The stun package is intended to use by package that implements extension
|
||||
// to STUN (e.g. TURN) or client/server applications.
|
||||
//
|
||||
// Most methods are designed to be zero allocations. If it is not enough,
|
||||
// low-level methods are available. On other hand, there are helpers that
|
||||
// reduce code repeat.
|
||||
//
|
||||
// See examples for Message for basic usage, or https://github.com/pion/turn
|
||||
// package for example of stun extension implementation.
|
||||
package stun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// bin is shorthand to binary.BigEndian.
|
||||
var bin = binary.BigEndian //nolint:gochecknoglobals
|
||||
|
||||
func readFullOrPanic(r io.Reader, v []byte) int {
|
||||
n, err := io.ReadFull(r, v)
|
||||
if err != nil {
|
||||
panic(err) //nolint
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func writeOrPanic(w io.Writer, v []byte) int {
|
||||
n, err := w.Write(v)
|
||||
if err != nil {
|
||||
panic(err) //nolint
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// IANA assigned ports for "stun" protocol.
|
||||
const (
|
||||
DefaultPort = 3478
|
||||
DefaultTLSPort = 5349
|
||||
)
|
||||
|
||||
type transactionIDSetter struct{}
|
||||
|
||||
func (transactionIDSetter) AddTo(m *Message) error {
|
||||
return m.NewTransactionID()
|
||||
}
|
||||
|
||||
// TransactionID is Setter for m.TransactionID.
|
||||
var TransactionID Setter = transactionIDSetter{} //nolint:gochecknoglobals
|
||||
132
server/vendor/github.com/pion/stun/textattrs.go
generated
vendored
Normal file
132
server/vendor/github.com/pion/stun/textattrs.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
// NewUsername returns Username with provided value.
|
||||
func NewUsername(username string) Username {
|
||||
return Username(username)
|
||||
}
|
||||
|
||||
// Username represents USERNAME attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.3
|
||||
type Username []byte
|
||||
|
||||
func (u Username) String() string {
|
||||
return string(u)
|
||||
}
|
||||
|
||||
const maxUsernameB = 513
|
||||
|
||||
// AddTo adds USERNAME attribute to message.
|
||||
func (u Username) AddTo(m *Message) error {
|
||||
return TextAttribute(u).AddToAs(m, AttrUsername, maxUsernameB)
|
||||
}
|
||||
|
||||
// GetFrom gets USERNAME from message.
|
||||
func (u *Username) GetFrom(m *Message) error {
|
||||
return (*TextAttribute)(u).GetFromAs(m, AttrUsername)
|
||||
}
|
||||
|
||||
// NewRealm returns Realm with provided value.
|
||||
// Must be SASL-prepared.
|
||||
func NewRealm(realm string) Realm {
|
||||
return Realm(realm)
|
||||
}
|
||||
|
||||
// Realm represents REALM attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.7
|
||||
type Realm []byte
|
||||
|
||||
func (n Realm) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
const maxRealmB = 763
|
||||
|
||||
// AddTo adds NONCE to message.
|
||||
func (n Realm) AddTo(m *Message) error {
|
||||
return TextAttribute(n).AddToAs(m, AttrRealm, maxRealmB)
|
||||
}
|
||||
|
||||
// GetFrom gets REALM from message.
|
||||
func (n *Realm) GetFrom(m *Message) error {
|
||||
return (*TextAttribute)(n).GetFromAs(m, AttrRealm)
|
||||
}
|
||||
|
||||
const softwareRawMaxB = 763
|
||||
|
||||
// Software is SOFTWARE attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.10
|
||||
type Software []byte
|
||||
|
||||
func (s Software) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// NewSoftware returns *Software from string.
|
||||
func NewSoftware(software string) Software {
|
||||
return Software(software)
|
||||
}
|
||||
|
||||
// AddTo adds Software attribute to m.
|
||||
func (s Software) AddTo(m *Message) error {
|
||||
return TextAttribute(s).AddToAs(m, AttrSoftware, softwareRawMaxB)
|
||||
}
|
||||
|
||||
// GetFrom decodes Software from m.
|
||||
func (s *Software) GetFrom(m *Message) error {
|
||||
return (*TextAttribute)(s).GetFromAs(m, AttrSoftware)
|
||||
}
|
||||
|
||||
// Nonce represents NONCE attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.8
|
||||
type Nonce []byte
|
||||
|
||||
// NewNonce returns new Nonce from string.
|
||||
func NewNonce(nonce string) Nonce {
|
||||
return Nonce(nonce)
|
||||
}
|
||||
|
||||
func (n Nonce) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
const maxNonceB = 763
|
||||
|
||||
// AddTo adds NONCE to message.
|
||||
func (n Nonce) AddTo(m *Message) error {
|
||||
return TextAttribute(n).AddToAs(m, AttrNonce, maxNonceB)
|
||||
}
|
||||
|
||||
// GetFrom gets NONCE from message.
|
||||
func (n *Nonce) GetFrom(m *Message) error {
|
||||
return (*TextAttribute)(n).GetFromAs(m, AttrNonce)
|
||||
}
|
||||
|
||||
// TextAttribute is helper for adding and getting text attributes.
|
||||
type TextAttribute []byte
|
||||
|
||||
// AddToAs adds attribute with type t to m, checking maximum length. If maxLen
|
||||
// is less than 0, no check is performed.
|
||||
func (v TextAttribute) AddToAs(m *Message, t AttrType, maxLen int) error {
|
||||
if err := CheckOverflow(t, len(v), maxLen); err != nil {
|
||||
return err
|
||||
}
|
||||
m.Add(t, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFromAs gets t attribute from m and appends its value to reseted v.
|
||||
func (v *TextAttribute) GetFromAs(m *Message, t AttrType) error {
|
||||
a, err := m.Get(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = a
|
||||
return nil
|
||||
}
|
||||
66
server/vendor/github.com/pion/stun/uattrs.go
generated
vendored
Normal file
66
server/vendor/github.com/pion/stun/uattrs.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import "errors"
|
||||
|
||||
// UnknownAttributes represents UNKNOWN-ATTRIBUTES attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.9
|
||||
type UnknownAttributes []AttrType
|
||||
|
||||
func (a UnknownAttributes) String() string {
|
||||
s := ""
|
||||
if len(a) == 0 {
|
||||
return "<nil>"
|
||||
}
|
||||
last := len(a) - 1
|
||||
for i, t := range a {
|
||||
s += t.String()
|
||||
if i != last {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// type size is 16 bit.
|
||||
const attrTypeSize = 4
|
||||
|
||||
// AddTo adds UNKNOWN-ATTRIBUTES attribute to message.
|
||||
func (a UnknownAttributes) AddTo(m *Message) error {
|
||||
v := make([]byte, 0, attrTypeSize*20) // 20 should be enough
|
||||
// If len(a.Types) > 20, there will be allocations.
|
||||
for i, t := range a {
|
||||
v = append(v, 0, 0, 0, 0) // 4 times by 0 (16 bits)
|
||||
first := attrTypeSize * i
|
||||
last := first + attrTypeSize
|
||||
bin.PutUint16(v[first:last], t.Value())
|
||||
}
|
||||
m.Add(AttrUnknownAttributes, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrBadUnknownAttrsSize means that UNKNOWN-ATTRIBUTES attribute value
|
||||
// has invalid length.
|
||||
var ErrBadUnknownAttrsSize = errors.New("bad UNKNOWN-ATTRIBUTES size")
|
||||
|
||||
// GetFrom parses UNKNOWN-ATTRIBUTES from message.
|
||||
func (a *UnknownAttributes) GetFrom(m *Message) error {
|
||||
v, err := m.Get(AttrUnknownAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(v)%attrTypeSize != 0 {
|
||||
return ErrBadUnknownAttrsSize
|
||||
}
|
||||
*a = (*a)[:0]
|
||||
first := 0
|
||||
for first < len(v) {
|
||||
last := first + attrTypeSize
|
||||
*a = append(*a, AttrType(bin.Uint16(v[first:last])))
|
||||
first = last
|
||||
}
|
||||
return nil
|
||||
}
|
||||
257
server/vendor/github.com/pion/stun/uri.go
generated
vendored
Normal file
257
server/vendor/github.com/pion/stun/uri.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
// ErrProtoType indicates an unsupported transport type was provided.
|
||||
ErrProtoType = errors.New("invalid transport protocol type")
|
||||
)
|
||||
|
||||
// SchemeType indicates the type of server used in the ice.URL structure.
|
||||
type SchemeType int
|
||||
|
||||
const (
|
||||
// SchemeTypeUnknown indicates an unknown or unsupported scheme.
|
||||
SchemeTypeUnknown SchemeType = iota
|
||||
|
||||
// SchemeTypeSTUN indicates the URL represents a STUN server.
|
||||
SchemeTypeSTUN
|
||||
|
||||
// SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
|
||||
SchemeTypeSTUNS
|
||||
|
||||
// SchemeTypeTURN indicates the URL represents a TURN server.
|
||||
SchemeTypeTURN
|
||||
|
||||
// SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
|
||||
SchemeTypeTURNS
|
||||
)
|
||||
|
||||
// NewSchemeType defines a procedure for creating a new SchemeType from a raw
|
||||
// string naming the scheme type.
|
||||
func NewSchemeType(raw string) SchemeType {
|
||||
switch raw {
|
||||
case "stun":
|
||||
return SchemeTypeSTUN
|
||||
case "stuns":
|
||||
return SchemeTypeSTUNS
|
||||
case "turn":
|
||||
return SchemeTypeTURN
|
||||
case "turns":
|
||||
return SchemeTypeTURNS
|
||||
default:
|
||||
return SchemeTypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (t SchemeType) String() string {
|
||||
switch t {
|
||||
case SchemeTypeSTUN:
|
||||
return "stun"
|
||||
case SchemeTypeSTUNS:
|
||||
return "stuns"
|
||||
case SchemeTypeTURN:
|
||||
return "turn"
|
||||
case SchemeTypeTURNS:
|
||||
return "turns"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// ProtoType indicates the transport protocol type that is used in the ice.URL
|
||||
// structure.
|
||||
type ProtoType int
|
||||
|
||||
const (
|
||||
// ProtoTypeUnknown indicates an unknown or unsupported protocol.
|
||||
ProtoTypeUnknown ProtoType = iota
|
||||
|
||||
// ProtoTypeUDP indicates the URL uses a UDP transport.
|
||||
ProtoTypeUDP
|
||||
|
||||
// ProtoTypeTCP indicates the URL uses a TCP transport.
|
||||
ProtoTypeTCP
|
||||
)
|
||||
|
||||
// NewProtoType defines a procedure for creating a new ProtoType from a raw
|
||||
// string naming the transport protocol type.
|
||||
func NewProtoType(raw string) ProtoType {
|
||||
switch raw {
|
||||
case "udp":
|
||||
return ProtoTypeUDP
|
||||
case "tcp":
|
||||
return ProtoTypeTCP
|
||||
default:
|
||||
return ProtoTypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
func (t ProtoType) String() string {
|
||||
switch t {
|
||||
case ProtoTypeUDP:
|
||||
return "udp"
|
||||
case ProtoTypeTCP:
|
||||
return "tcp"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// URI represents a STUN (rfc7064) or TURN (rfc7065) URI
|
||||
type URI struct {
|
||||
Scheme SchemeType
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Proto ProtoType
|
||||
}
|
||||
|
||||
// ParseURI 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.
|
||||
func ParseURI(raw string) (*URI, error) { //nolint:gocognit
|
||||
rawParts, err := url.Parse(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var u URI
|
||||
u.Scheme = NewSchemeType(rawParts.Scheme)
|
||||
if u.Scheme == SchemeTypeUnknown {
|
||||
return nil, ErrSchemeType
|
||||
}
|
||||
|
||||
var rawPort string
|
||||
if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil {
|
||||
var e *net.AddrError
|
||||
if errors.As(err, &e) {
|
||||
if e.Err == "missing port in address" {
|
||||
nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque
|
||||
switch {
|
||||
case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN:
|
||||
nextRawURL += ":3478"
|
||||
if rawParts.RawQuery != "" {
|
||||
nextRawURL += "?" + rawParts.RawQuery
|
||||
}
|
||||
return ParseURI(nextRawURL)
|
||||
case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS:
|
||||
nextRawURL += ":5349"
|
||||
if rawParts.RawQuery != "" {
|
||||
nextRawURL += "?" + rawParts.RawQuery
|
||||
}
|
||||
return ParseURI(nextRawURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Host == "" {
|
||||
return nil, ErrHost
|
||||
}
|
||||
|
||||
if u.Port, err = strconv.Atoi(rawPort); err != nil {
|
||||
return nil, ErrPort
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case SchemeTypeSTUN:
|
||||
qArgs, err := url.ParseQuery(rawParts.RawQuery)
|
||||
if err != nil || len(qArgs) > 0 {
|
||||
return nil, ErrSTUNQuery
|
||||
}
|
||||
u.Proto = ProtoTypeUDP
|
||||
case SchemeTypeSTUNS:
|
||||
qArgs, err := url.ParseQuery(rawParts.RawQuery)
|
||||
if err != nil || len(qArgs) > 0 {
|
||||
return nil, ErrSTUNQuery
|
||||
}
|
||||
u.Proto = ProtoTypeTCP
|
||||
case SchemeTypeTURN:
|
||||
proto, err := parseProto(rawParts.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Proto = proto
|
||||
if u.Proto == ProtoTypeUnknown {
|
||||
u.Proto = ProtoTypeUDP
|
||||
}
|
||||
case SchemeTypeTURNS:
|
||||
proto, err := parseProto(rawParts.RawQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u.Proto = proto
|
||||
if u.Proto == ProtoTypeUnknown {
|
||||
u.Proto = ProtoTypeTCP
|
||||
}
|
||||
|
||||
case SchemeTypeUnknown:
|
||||
}
|
||||
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func parseProto(raw string) (ProtoType, error) {
|
||||
qArgs, err := url.ParseQuery(raw)
|
||||
if err != nil || len(qArgs) > 1 {
|
||||
return ProtoTypeUnknown, ErrInvalidQuery
|
||||
}
|
||||
|
||||
var proto ProtoType
|
||||
if rawProto := qArgs.Get("transport"); rawProto != "" {
|
||||
if proto = NewProtoType(rawProto); proto == ProtoType(0) {
|
||||
return ProtoTypeUnknown, ErrProtoType
|
||||
}
|
||||
return proto, nil
|
||||
}
|
||||
|
||||
if len(qArgs) > 0 {
|
||||
return ProtoTypeUnknown, ErrInvalidQuery
|
||||
}
|
||||
|
||||
return proto, nil
|
||||
}
|
||||
|
||||
func (u URI) String() string {
|
||||
rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port))
|
||||
if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS {
|
||||
rawURL += "?transport=" + u.Proto.String()
|
||||
}
|
||||
return rawURL
|
||||
}
|
||||
|
||||
// IsSecure returns whether the this URL's scheme describes secure scheme or not.
|
||||
func (u URI) IsSecure() bool {
|
||||
return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS
|
||||
}
|
||||
150
server/vendor/github.com/pion/stun/xoraddr.go
generated
vendored
Normal file
150
server/vendor/github.com/pion/stun/xoraddr.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package stun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/pion/transport/v2/utils/xor"
|
||||
)
|
||||
|
||||
const (
|
||||
familyIPv4 uint16 = 0x01
|
||||
familyIPv6 uint16 = 0x02
|
||||
)
|
||||
|
||||
// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
|
||||
//
|
||||
// RFC 5389 Section 15.2
|
||||
type XORMappedAddress struct {
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a XORMappedAddress) String() string {
|
||||
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
|
||||
}
|
||||
|
||||
// isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4.
|
||||
func isIPv4(ip net.IP) bool {
|
||||
// Optimized for performance. Copied from net.IP.To4.
|
||||
return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff
|
||||
}
|
||||
|
||||
// Is p all zeros?
|
||||
func isZeros(p net.IP) bool {
|
||||
for i := 0; i < len(p); i++ {
|
||||
if p[i] != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}.
|
||||
var ErrBadIPLength = errors.New("invalid length of IP value")
|
||||
|
||||
// AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute.
|
||||
func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error {
|
||||
var (
|
||||
family = familyIPv4
|
||||
ip = a.IP
|
||||
)
|
||||
if len(a.IP) == net.IPv6len {
|
||||
if isIPv4(ip) {
|
||||
ip = ip[12:16] // like in ip.To4()
|
||||
} else {
|
||||
family = familyIPv6
|
||||
}
|
||||
} else if len(ip) != net.IPv4len {
|
||||
return ErrBadIPLength
|
||||
}
|
||||
value := make([]byte, 32+128)
|
||||
value[0] = 0 // first 8 bits are zeroes
|
||||
xorValue := make([]byte, net.IPv6len)
|
||||
copy(xorValue[4:], m.TransactionID[:])
|
||||
bin.PutUint32(xorValue[0:4], magicCookie)
|
||||
bin.PutUint16(value[0:2], family)
|
||||
bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
|
||||
xor.XorBytes(value[4:4+len(ip)], ip, xorValue)
|
||||
m.Add(t, value[:4+len(ip)])
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
|
||||
// if len(a.IP) is invalid.
|
||||
func (a XORMappedAddress) AddTo(m *Message) error {
|
||||
return a.AddToAs(m, AttrXORMappedAddress)
|
||||
}
|
||||
|
||||
// GetFromAs decodes XOR-MAPPED-ADDRESS attribute value in message
|
||||
// getting it as for t type.
|
||||
func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
|
||||
v, err := m.Get(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
family := bin.Uint16(v[0:2])
|
||||
if family != familyIPv6 && family != familyIPv4 {
|
||||
return newDecodeErr("xor-mapped address", "family",
|
||||
fmt.Sprintf("bad value %d", family),
|
||||
)
|
||||
}
|
||||
ipLen := net.IPv4len
|
||||
if family == familyIPv6 {
|
||||
ipLen = net.IPv6len
|
||||
}
|
||||
// Ensuring len(a.IP) == ipLen and reusing a.IP.
|
||||
if len(a.IP) < ipLen {
|
||||
a.IP = a.IP[:cap(a.IP)]
|
||||
for len(a.IP) < ipLen {
|
||||
a.IP = append(a.IP, 0)
|
||||
}
|
||||
}
|
||||
a.IP = a.IP[:ipLen]
|
||||
for i := range a.IP {
|
||||
a.IP[i] = 0
|
||||
}
|
||||
if len(v) <= 4 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := CheckOverflow(t, len(v[4:]), len(a.IP)); err != nil {
|
||||
return err
|
||||
}
|
||||
a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16)
|
||||
xorValue := make([]byte, 4+TransactionIDSize)
|
||||
bin.PutUint32(xorValue[0:4], magicCookie)
|
||||
copy(xorValue[4:], m.TransactionID[:])
|
||||
xor.XorBytes(a.IP, v[4:], xorValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
|
||||
// error if any. While decoding, a.IP is reused if possible and can be
|
||||
// rendered to invalid state (e.g. if a.IP was set to IPv6 and then
|
||||
// IPv4 value were decoded into it), be careful.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// expectedIP := net.ParseIP("213.141.156.236")
|
||||
// expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
|
||||
// expectedPort := 21254
|
||||
// addr := &XORMappedAddress{
|
||||
// IP: expectedIP,
|
||||
// Port: expectedPort,
|
||||
// }
|
||||
// // addr were added to message that is decoded as newMessage
|
||||
// // ...
|
||||
//
|
||||
// addr.GetFrom(newMessage)
|
||||
// addr.IP.String() // 213.141.156.236, net.IPv4Len
|
||||
// expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
|
||||
// // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
|
||||
func (a *XORMappedAddress) GetFrom(m *Message) error {
|
||||
return a.GetFromAs(m, AttrXORMappedAddress)
|
||||
}
|
||||
Reference in New Issue
Block a user