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

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

------

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

View file

@ -1,9 +0,0 @@
coverage:
status:
patch: off
project:
default:
# basic
target: 98
threshold: null
base: auto

View file

@ -1,17 +1,28 @@
*-fuzz.zip
.idea
benchmark.*.write
*.test
*.out
# 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]
bench.go-*
PACKAGES
cmd/stun-cli/stun-cli
cmd/stun-decode/stun-decode
cmd/stun-bench/stun-bench
cmd/stun-nat-behaviour/stun-nat-behaviour
coverage.txt
e2e/dump.pcap
e2e/log-*.txt
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

View file

@ -1,93 +1,137 @@
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 15
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
misspell:
locale: US
lll:
line-length: 140
goimports:
local-prefixes: github.com/pion
gocritic:
enabled-tags:
- performance
- style
- experimental
disabled-checks:
- commentedOutCode
- sloppyReassign
issues:
exclude:
- "`assertHMACSize` - `blocksize` always receives `64`"
exclude-rules:
- text: "string `<nil>`"
linters:
- goconst
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- goconst
# Ease some gocritic warnings on test files.
- path: _test\.go
text: "(unnamedResult|exitAfterDefer|unlambda)"
linters:
- gocritic
# Exclude known linters from partially hard-vendored code,
# which is impossible to exclude via "nolint" comments.
- path: internal/hmac/
text: "weak cryptographic primitive"
linters:
- gosec
- path: internal/hmac/
text: "Write\\` is not checked"
linters:
- errcheck
# Ease linting on benchmarking code.
- path: cmd/stun-bench/
linters:
- gosec
- errcheck
- unparam
- path: ^cmd/
linters:
- gocyclo
- path: ^cmd/
text: "(unnamedResult|exitAfterDefer)"
linters:
- gocritic
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-all: true
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:
- funlen
- gochecknoglobals
- godox
- prealloc
- scopelint
- 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:
- e2e
- fuzz
- testdata
- api
skip-dirs-use-default: false

View file

@ -1,39 +1,5 @@
before:
hooks:
- go mod tidy
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
builds:
- binary: stun-not-behavior
id: stun-not-behavior
goos:
- darwin
- windows
- linux
- freebsd
goarch:
- amd64
- 386
env:
- CGO_ENABLED=0
main: ./cmd/stun-nat-behavior
- skip: true

View file

@ -1,135 +0,0 @@
#
# DO NOT EDIT THIS FILE DIRECTLY
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
dist: bionic
language: go
branches:
only:
- master
env:
global:
- GO111MODULE=on
- GOLANGCI_LINT_VERSION=1.19.1
cache:
directories:
- ${HOME}/.cache/go-build
- ${GOPATH}/pkg/mod
npm: true
yarn: true
_lint_job: &lint_job
env: CACHE_NAME=lint
before_install:
- if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
install: skip
before_script:
- |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
| bash -s - -b $GOPATH/bin v${GOLANGCI_LINT_VERSION}
script:
- bash .github/assert-contributors.sh
- bash .github/lint-disallowed-functions-in-library.sh
- bash .github/lint-commit-message.sh
- bash .github/lint-filename.sh
- golangci-lint run ./...
_test_job: &test_job
env: CACHE_NAME=test
before_install:
- if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
- go mod download
install:
- go build ./...
script:
- testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
- coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
- |
go test \
-coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
${TEST_EXTRA_ARGS:-} \
-v -race ${testpkgs}
- if [ -n "${TEST_HOOK}" ]; then ${TEST_HOOK}; fi
after_success:
- travis_retry bash <(curl -s https://codecov.io/bash) -c -F go
_test_i386_job: &test_i386_job
env: CACHE_NAME=test386
services: docker
before_install:
- if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
script:
- testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
- |
docker run \
-u $(id -u):$(id -g) \
-e "GO111MODULE=on" \
-e "CGO_ENABLED=0" \
-v ${PWD}:/go/src/github.com/pion/$(basename ${PWD}) \
-v ${HOME}/gopath/pkg/mod:/go/pkg/mod \
-v ${HOME}/.cache/go-build:/.cache/go-build \
-w /go/src/github.com/pion/$(basename ${PWD}) \
-it i386/golang:${GO_VERSION}-alpine \
/usr/local/go/bin/go test \
${TEST_EXTRA_ARGS:-} \
-v ${testpkgs}
_test_wasm_job: &test_wasm_job
env: CACHE_NAME=wasm
language: node_js
node_js: 12
before_install:
- if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
- if ${SKIP_WASM_TEST:-false}; then exit 0; fi
install:
# Manually download and install Go instead of using gimme.
# It looks like gimme Go causes some errors on go-test for Wasm.
- curl -sSfL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz | tar -C ~ -xzf -
- export GOROOT=${HOME}/go
- export PATH=${GOROOT}/bin:${PATH}
- yarn install
- export GO_JS_WASM_EXEC=${GO_JS_WASM_EXEC:-${GOROOT}/misc/wasm/go_js_wasm_exec}
script:
- testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
- coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
- |
GOOS=js GOARCH=wasm go test \
-coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
-exec="${GO_JS_WASM_EXEC}" \
-v ${testpkgs}
after_success:
- travis_retry bash <(curl -s https://codecov.io/bash) -c -F wasm
jobs:
include:
- <<: *lint_job
name: Lint 1.14
go: 1.14
- <<: *test_job
name: Test 1.13
go: 1.13
- <<: *test_job
name: Test 1.14
go: 1.14
- <<: *test_i386_job
name: Test i386 1.13
env: GO_VERSION=1.13
go: 1.14 # version for host environment used to go list
- <<: *test_i386_job
name: Test i386 1.14
env: GO_VERSION=1.14
go: 1.14 # version for host environment used to go list
- <<: *test_wasm_job
name: Test WASM 1.13
env: GO_VERSION=1.13
- <<: *test_wasm_job
name: Test WASM 1.14
env: GO_VERSION=1.14
notifications:
email: false

View file

@ -1,10 +0,0 @@
Sean DuBois <https://github.com/Sean-Der>
Raphael Randschau <https://github.com/nicolai86>
Aleksandr Razumov <ar@cydev.ru>
Aliaksandr Valialkin <valyala@gmail.com>
Michiel De Backker <https://github.com/backkem>
Y.Horie <https://github.com/u5surf>
songjiayang <https://github.com/songjiayang>
The gortc project
The IETF Trust
The Go Authors

View 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

View file

@ -1,5 +0,0 @@
FROM golang:1.14
COPY . /go/src/github.com/pion/stun
RUN go test github.com/pion/stun

View file

@ -1,4 +1,6 @@
Copyright 2018 Pion LLC
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:

View file

@ -1,3 +1,6 @@
# 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)
@ -9,22 +12,6 @@ bench:
go test -bench .
bench-record:
$(GO) test -bench . > "benchmarks/stun-go-$(GO_VERSION).txt"
fuzz-prepare-msg:
go-fuzz-build -func FuzzMessage -o stun-msg-fuzz.zip github.com/pion/stun
fuzz-prepare-typ:
go-fuzz-build -func FuzzType -o stun-typ-fuzz.zip github.com/pion/stun
fuzz-prepare-setters:
go-fuzz-build -func FuzzSetters -o stun-setters-fuzz.zip github.com/pion/stun
fuzz-msg:
go-fuzz -bin=./stun-msg-fuzz.zip -workdir=fuzz/stun-msg
fuzz-typ:
go-fuzz -bin=./stun-typ-fuzz.zip -workdir=fuzz/stun-typ
fuzz-setters:
go-fuzz -bin=./stun-setters-fuzz.zip -workdir=fuzz/stun-setters
fuzz-test:
go test -tags gofuzz -run TestFuzz -v .
fuzz-reset-setters:
rm -f -v -r stun-setters-fuzz.zip fuzz/stun-setters
lint:
@golangci-lint run ./...
@echo "ok"
@ -39,23 +26,14 @@ bench-compare:
go test -bench . > bench.go-16
go-tip test -bench . > bench.go-tip
@benchcmp bench.go-16 bench.go-tip
install-fuzz:
go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
go get github.com/dvyukov/go-fuzz/go-fuzz
install:
go get gortc.io/api
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
docker-build:
docker build -t pion/stun .
test-integration:
@cd e2e && bash ./test.sh
prepush: assert test lint test-integration
prepush: test lint test-integration
check-api:
@cd api && bash ./check.sh
assert:
bash .github/assert-contributors.sh
bash .github/lint-disallowed-functions-in-library.sh
bash .github/lint-commit-message.sh
test:
@./go.test.sh
clean:

View file

@ -6,52 +6,21 @@
<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://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></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>
<a href="https://travis-ci.org/pion/stun"><img src="https://travis-ci.org/pion/stun.svg?branch=master" alt="Build Status"></a>
<a href="https://pkg.go.dev/github.com/pion/stun"><img src="https://godoc.org/github.com/pion/stun?status.svg" alt="GoDoc"></a>
<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="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
<br>
### Roadmap
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
### Community
Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
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:
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
* [Raphael Randschau](https://github.com/nicolai86) - *STUN client*
* [Michiel De Backker](https://github.com/backkem) - *Minor fixes*
* [Y.Horie](https://github.com/u5surf) - *Fix lint issues*
* [Aleksandr Razumov](https://github.com/ernado) - *The v0.3 version*
* [songjiayang](https://github.com/songjiayang)
* [Adam Kiss](https://github.com/masterada)
* [Moises Marangoni](https://github.com/Moisesbr)
* [Yutaka Takeda](https://github.com/enobufs)
* [Hugo Arregui](https://github.com/hugoArregui)
* [Maanas Royy](https://github.com/maanas)
* [Atsushi Watanabe](https://github.com/at-wat)
* [Cecylia Bocovich](https://github.com/cohosh)
* [Christian Muehlhaeuser](https://github.com/muesli)
# STUN
Package stun implements Session Traversal Utilities for NAT (STUN) [[RFC5389](https://tools.ietf.org/html/rfc5389)]
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
### 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
@ -64,8 +33,14 @@ import (
)
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.Dial("udp", "stun.l.google.com:19302")
c, err := stun.DialURI(u, &stun.DialConfig{})
if err != nil {
panic(err)
}
@ -88,30 +63,43 @@ func main() {
}
```
## Supported RFCs
- [x] [RFC 5389](https://tools.ietf.org/html/rfc5389) — Session Traversal Utilities for NAT
- [x] [RFC 5769](https://tools.ietf.org/html/rfc5769) — Test Vectors for STUN
- [x] [RFC 6062](https://tools.ietf.org/html/rfc6062) — TURN extensions for TCP allocations
- [x] [RFC 7064](https://tools.ietf.org/html/rfc7064) — STUN URI
- [x] (TLS-over-)TCP client support
- [ ] [ALTERNATE-SERVER](https://tools.ietf.org/html/rfc5389#section-11) support [#48](https://github.com/pion/stun/issues/48)
- [ ] [RFC 5780](https://tools.ietf.org/html/rfc5780) — NAT Behavior Discovery Using STUN [#49](https://github.com/pion/stun/issues/49)
### 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
# Stability
#### 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.
# RFC 3489 notes
RFC 5389 obsoletes RFC 3489, so implementation was ignored by purpose, however,
RFC 3489 can be easily implemented as separate package.
# Requirements
### Requirements
Go 1.12 is currently supported and tested in CI.
# Testing
### 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)
@ -123,62 +111,76 @@ See [TeamCity project](https://tc.gortc.io/project.html?projectId=stun&guest=1)
for more information. Also the Wireshark `.pcap` files are available for e2e test in
artifacts for build.
# Benchmarks
### Benchmarks
Intel(R) Core(TM) i7-8700K:
```
version: 1.16.5
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 30000000 36.40 ns/op 0 B/op 0 allocs/op
BenchmarkAlternateServer_AddTo-12 50000000 36.70 ns/op 0 B/op 0 allocs/op
BenchmarkAgent_GC-12 500000 2552.00 ns/op 0 B/op 0 allocs/op
BenchmarkAgent_Process-12 50000000 38.00 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_GetNotFound-12 200000000 6.90 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_Get-12 200000000 7.61 ns/op 0 B/op 0 allocs/op
BenchmarkClient_Do-12 2000000 1072.00 ns/op 0 B/op 0 allocs/op
BenchmarkErrorCode_AddTo-12 20000000 67.00 ns/op 0 B/op 0 allocs/op
BenchmarkErrorCodeAttribute_AddTo-12 30000000 52.20 ns/op 0 B/op 0 allocs/op
BenchmarkErrorCodeAttribute_GetFrom-12 100000000 12.00 ns/op 0 B/op 0 allocs/op
BenchmarkFingerprint_AddTo-12 20000000 102.00 ns/op 430.08 MB/s 0 B/op 0 allocs/op
BenchmarkFingerprint_Check-12 30000000 54.80 ns/op 948.38 MB/s 0 B/op 0 allocs/op
BenchmarkBuildOverhead/Build-12 5000000 333.00 ns/op 0 B/op 0 allocs/op
BenchmarkBuildOverhead/BuildNonPointer-12 3000000 536.00 ns/op 100 B/op 4 allocs/op
BenchmarkBuildOverhead/Raw-12 10000000 181.00 ns/op 0 B/op 0 allocs/op
BenchmarkMessageIntegrity_AddTo-12 1000000 1053.00 ns/op 18.98 MB/s 0 B/op 0 allocs/op
BenchmarkMessageIntegrity_Check-12 1000000 1135.00 ns/op 28.17 MB/s 0 B/op 0 allocs/op
BenchmarkMessage_Write-12 100000000 27.70 ns/op 1011.09 MB/s 0 B/op 0 allocs/op
BenchmarkMessageType_Value-12 2000000000 0.49 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_WriteTo-12 100000000 12.80 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_ReadFrom-12 50000000 25.00 ns/op 801.19 MB/s 0 B/op 0 allocs/op
BenchmarkMessage_ReadBytes-12 100000000 18.00 ns/op 1113.03 MB/s 0 B/op 0 allocs/op
BenchmarkIsMessage-12 2000000000 1.08 ns/op 18535.57 MB/s 0 B/op 0 allocs/op
BenchmarkMessage_NewTransactionID-12 2000000 673.00 ns/op 0 B/op 0 allocs/op
BenchmarkMessageFull-12 5000000 316.00 ns/op 0 B/op 0 allocs/op
BenchmarkMessageFullHardcore-12 20000000 88.90 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_WriteHeader-12 200000000 8.18 ns/op 0 B/op 0 allocs/op
BenchmarkMessage_CloneTo-12 30000000 37.90 ns/op 1795.32 MB/s 0 B/op 0 allocs/op
BenchmarkMessage_AddTo-12 300000000 4.77 ns/op 0 B/op 0 allocs/op
BenchmarkDecode-12 100000000 22.00 ns/op 0 B/op 0 allocs/op
BenchmarkUsername_AddTo-12 50000000 23.20 ns/op 0 B/op 0 allocs/op
BenchmarkUsername_GetFrom-12 100000000 17.90 ns/op 0 B/op 0 allocs/op
BenchmarkNonce_AddTo-12 50000000 34.40 ns/op 0 B/op 0 allocs/op
BenchmarkNonce_AddTo_BadLength-12 200000000 8.29 ns/op 0 B/op 0 allocs/op
BenchmarkNonce_GetFrom-12 100000000 17.50 ns/op 0 B/op 0 allocs/op
BenchmarkUnknownAttributes/AddTo-12 30000000 48.10 ns/op 0 B/op 0 allocs/op
BenchmarkUnknownAttributes/GetFrom-12 100000000 20.90 ns/op 0 B/op 0 allocs/op
BenchmarkXOR-12 50000000 25.80 ns/op 39652.86 MB/s 0 B/op 0 allocs/op
BenchmarkXORSafe-12 3000000 515.00 ns/op 1988.04 MB/s 0 B/op 0 allocs/op
BenchmarkXORFast-12 20000000 73.40 ns/op 13959.30 MB/s 0 B/op 0 allocs/op
BenchmarkXORMappedAddress_AddTo-12 20000000 56.70 ns/op 0 B/op 0 allocs/op
BenchmarkXORMappedAddress_GetFrom-12 50000000 37.40 ns/op 0 B/op 0 allocs/op
ok github.com/pion/stun 76.868s
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

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -26,6 +29,14 @@ type AlternateServer struct {
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
@ -37,20 +48,21 @@ type OtherAddress struct {
// AddTo adds ALTERNATE-SERVER attribute to message.
func (s *AlternateServer) AddTo(m *Message) error {
a := (*MappedAddress)(s)
return a.addAs(m, AttrAlternateServer)
return a.AddToAs(m, AttrAlternateServer)
}
// GetFrom decodes ALTERNATE-SERVER from message.
func (s *AlternateServer) GetFrom(m *Message) error {
a := (*MappedAddress)(s)
return a.getAs(m, AttrAlternateServer)
return a.GetFromAs(m, AttrAlternateServer)
}
func (a MappedAddress) String() string {
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}
func (a *MappedAddress) getAs(m *Message, t AttrType) error {
// 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
@ -84,7 +96,8 @@ func (a *MappedAddress) getAs(m *Message, t AttrType) error {
return nil
}
func (a *MappedAddress) addAs(m *Message, t AttrType) error {
// 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
@ -109,26 +122,42 @@ func (a *MappedAddress) addAs(m *Message, t AttrType) error {
// AddTo adds MAPPED-ADDRESS to message.
func (a *MappedAddress) AddTo(m *Message) error {
return a.addAs(m, AttrMappedAddress)
return a.AddToAs(m, AttrMappedAddress)
}
// GetFrom decodes MAPPED-ADDRESS from message.
func (a *MappedAddress) GetFrom(m *Message) error {
return a.getAs(m, AttrMappedAddress)
return a.GetFromAs(m, AttrMappedAddress)
}
// AddTo adds OTHER-ADDRESS attribute to message.
func (o *OtherAddress) AddTo(m *Message) error {
a := (*MappedAddress)(o)
return a.addAs(m, AttrOtherAddress)
return a.AddToAs(m, AttrOtherAddress)
}
// GetFrom decodes OTHER-ADDRESS from message.
func (o *OtherAddress) GetFrom(m *Message) error {
a := (*MappedAddress)(o)
return a.getAs(m, AttrOtherAddress)
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))
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -7,13 +10,15 @@ import (
)
// NoopHandler just discards any event.
var NoopHandler Handler = func(e 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
h = NoopHandler()
}
a := &Agent{
transactions: make(map[transactionID]agentTransaction),

View file

@ -1,22 +0,0 @@
version: "{build}"
platform: x64
branches:
only:
- master
skip_tags: true
clone_folder: c:\gopath\src\github.com\pion\stun
environment:
GOPATH: c:\gopath
GOVERSION: 1.12
install:
- go version
- go get -v -t .
build_script:
- go test -v .

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -76,8 +79,20 @@ const (
// Attributes from RFC 5780 NAT Behavior Discovery
const (
AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS
AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST
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.
@ -95,45 +110,58 @@ 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)
}
var attrNames = 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",
AttrOtherAddress: "OTHER-ADDRESS",
AttrChangeRequest: "CHANGE-REQUEST",
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",
AttrOrigin: "ORIGIN",
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]
s, ok := attrNames()[t]
if !ok {
// Just return hex representation of unknown attribute type.
return fmt.Sprintf("0x%x", uint16(t))
@ -219,8 +247,8 @@ func nearestPaddedValueLength(l int) int {
// 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 {
return AttrXORMappedAddress // new: 0x0020
if val == 0x8020 { // draft-ietf-behave-rfc3489bis-02, MS-TURN
return AttrXORMappedAddress // new: 0x0020 (from draft-ietf-behave-rfc3489bis-03 on)
}
return AttrType(val)
}

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build debug
// +build debug
package stun

View file

@ -1,8 +1,16 @@
// 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"
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 {
@ -28,7 +36,7 @@ func checkFingerprint(got, expected uint32) error {
// IsAttrSizeInvalid returns true if error means that attribute size is invalid.
func IsAttrSizeInvalid(err error) bool {
return err == ErrAttributeSizeInvalid
return errors.Is(err, ErrAttributeSizeInvalid)
}
// CheckOverflow returns ErrAttributeSizeOverflow if got is bigger that max.
@ -41,5 +49,5 @@ func CheckOverflow(_ AttrType, got, max int) error {
// IsAttrSizeOverflow returns true if error means that attribute size is too big.
func IsAttrSizeOverflow(err error) bool {
return err == ErrAttributeSizeOverflow
return errors.Is(err, ErrAttributeSizeOverflow)
}

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build debug
// +build debug
package stun

View file

@ -1,17 +1,29 @@
// 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) {
@ -22,6 +34,77 @@ func Dial(network, address string) (*Client, error) {
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")
@ -79,8 +162,10 @@ func WithCollector(coll Collector) ClientOption {
// WithNoConnClose prevents client from closing underlying connection when
// the Close() method is called.
var WithNoConnClose ClientOption = func(c *Client) {
c.closeConn = false
func WithNoConnClose() ClientOption {
return func(c *Client) {
c.closeConn = false
}
}
// WithNoRetransmit disables retransmissions and sets RTO to
@ -116,7 +201,7 @@ func NewClient(conn Connection, options ...ClientOption) (*Client, error) {
c := &Client{
close: make(chan struct{}),
c: conn,
clock: systemClock,
clock: systemClock(),
rto: int64(defaultRTO),
rtoRate: defaultTimeoutRate,
t: make(map[transactionID]*clientTransaction, 100),
@ -157,7 +242,7 @@ func clientFinalizer(c *Client) {
return
}
err := c.Close()
if err == ErrClientClosed {
if errors.Is(err, ErrClientClosed) {
return
}
if err == nil {
@ -225,7 +310,7 @@ func (t *clientTransaction) handle(e Event) {
}
}
var clientTransactionPool = &sync.Pool{
var clientTransactionPool = &sync.Pool{ //nolint:gochecknoglobals
New: func() interface{} {
return &clientTransaction{
raw: make([]byte, 1500),
@ -234,7 +319,7 @@ var clientTransactionPool = &sync.Pool{
}
func acquireClientTransaction() *clientTransaction {
return clientTransactionPool.Get().(*clientTransaction)
return clientTransactionPool.Get().(*clientTransaction) //nolint:forcetypeassert
}
func putClientTransaction(t *clientTransaction) {
@ -275,7 +360,9 @@ type systemClockService struct{}
func (systemClockService) Now() time.Time { return time.Now() }
var systemClock = systemClockService{}
func systemClock() systemClockService {
return systemClockService{}
}
// SetRTO sets current RTO value.
func (c *Client) SetRTO(rto time.Duration) {
@ -284,6 +371,8 @@ func (c *Client) SetRTO(rto time.Duration) {
// 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
@ -294,6 +383,8 @@ func (e StopErr) Error() string {
}
// CloseErr indicates client close failure.
//
//nolint:errname
type CloseErr struct {
AgentErr error
ConnectionErr error
@ -301,7 +392,7 @@ type CloseErr struct {
func sprintErr(err error) string {
if err == nil {
return "<nil>"
return "<nil>" //nolint:goconst
}
return err.Error()
}
@ -322,7 +413,7 @@ func (c *Client) readUntilClosed() {
}
_, err := m.ReadFrom(c.c)
if err == nil {
if pErr := c.a.Process(m); pErr == ErrAgentClosed {
if pErr := c.a.Process(m); errors.Is(pErr, ErrAgentClosed) {
return
}
}
@ -330,10 +421,10 @@ func (c *Client) readUntilClosed() {
}
func closedOrPanic(err error) {
if err == nil || err == ErrAgentClosed {
if err == nil || errors.Is(err, ErrAgentClosed) {
return
}
panic(err) // nolint
panic(err) //nolint
}
type tickerCollector struct {
@ -425,7 +516,7 @@ type callbackWaitHandler struct {
func (s *callbackWaitHandler) HandleEvent(e Event) {
s.cond.L.Lock()
if s.callback == nil {
panic("s.callback is nil") // nolint
panic("s.callback is nil") //nolint
}
s.callback(e)
s.processed = true
@ -445,7 +536,7 @@ func (s *callbackWaitHandler) wait() {
func (s *callbackWaitHandler) setCallback(f func(event Event)) {
if f == nil {
panic("f is nil") // nolint
panic("f is nil") //nolint
}
s.cond.L.Lock()
s.callback = f
@ -455,7 +546,7 @@ func (s *callbackWaitHandler) setCallback(f func(event Event)) {
s.cond.L.Unlock()
}
var callbackWaitHandlerPool = sync.Pool{
var callbackWaitHandlerPool = sync.Pool{ //nolint:gochecknoglobals
New: func() interface{} {
return &callbackWaitHandler{
cond: sync.NewCond(new(sync.Mutex)),
@ -485,7 +576,7 @@ func (c *Client) Do(m *Message, f func(Event)) error {
if f == nil {
return c.Indicate(m)
}
h := callbackWaitHandlerPool.Get().(*callbackWaitHandler)
h := callbackWaitHandlerPool.Get().(*callbackWaitHandler) //nolint:forcetypeassert
h.setCallback(f)
defer func() {
callbackWaitHandlerPool.Put(h)
@ -509,7 +600,7 @@ type buffer struct {
buf []byte
}
var bufferPool = &sync.Pool{
var bufferPool = &sync.Pool{ //nolint:gochecknoglobals
New: func() interface{} {
return &buffer{buf: make([]byte, 2048)}
},
@ -527,7 +618,7 @@ func (c *Client) handleAgentCallback(e Event) {
}
c.mux.Unlock()
if !found {
if c.handler != nil && e.Error != ErrTransactionStopped {
if c.handler != nil && !errors.Is(e.Error, ErrTransactionStopped) {
c.handler(e)
}
// Ignoring.
@ -541,7 +632,7 @@ func (c *Client) handleAgentCallback(e Event) {
}
// Doing re-transmission.
t.attempt++
b := bufferPool.Get().(*buffer)
b := bufferPool.Get().(*buffer) //nolint:forcetypeassert
b.buf = b.buf[:copy(b.buf[:cap(b.buf)], t.raw)]
defer bufferPool.Put(b)
var (

View 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/**/*"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -29,7 +32,7 @@ const (
// AddTo adds ERROR-CODE to m.
func (c ErrorCodeAttribute) AddTo(m *Message) error {
value := make([]byte, 0, errorCodeReasonMaxB)
value := make([]byte, 0, errorCodeReasonStart+errorCodeReasonMaxB)
if err := CheckOverflow(AttrErrorCode,
len(c.Reason)+errorCodeReasonStart,
errorCodeReasonMaxB+errorCodeReasonStart,
@ -131,6 +134,7 @@ const (
CodePeerAddrFamilyMismatch ErrorCode = 443 // Peer Address Family Mismatch
)
//nolint:gochecknoglobals
var errorReasons = map[ErrorCode][]byte{
CodeTryAlternate: []byte("Try Alternate"),
CodeBadRequest: []byte("Bad Request"),

View file

@ -1,8 +1,13 @@
// 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
@ -50,7 +55,6 @@ func newDecodeErr(parent, children, message string) *DecodeErr {
}
}
// TODO(ar): rewrite errors to be more precise.
func newAttrDecodeErr(children, message string) *DecodeErr {
return newDecodeErr("attribute", children, message)
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -17,9 +20,9 @@ var ErrFingerprintMismatch = errors.New("fingerprint check failed")
//
// Example:
//
// m := New()
// Fingerprint.AddTo(m)
var Fingerprint FingerprintAttr
// m := New()
// Fingerprint.AddTo(m)
var Fingerprint FingerprintAttr //nolint:gochecknoglobals
const (
fingerprintXORValue uint32 = 0x5354554e //nolint:staticcheck

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build debug
// +build debug
package stun

View file

@ -1,140 +0,0 @@
// +build gofuzz
package stun
import (
"encoding/binary"
"fmt"
)
var (
m = New()
)
// FuzzMessage is go-fuzz endpoint for message.
func FuzzMessage(data []byte) int {
m.Reset()
// fuzzer dont know about cookies
binary.BigEndian.PutUint32(data[4:8], magicCookie)
// trying to read data as message
if _, err := m.Write(data); err != nil {
return 0
}
m2 := New()
if _, err := m2.Write(m.Raw); err != nil {
panic(err) // nolint
}
if m2.TransactionID != m.TransactionID {
panic("transaction ID mismatch") // nolint
}
if m2.Type != m.Type {
panic("type missmatch") // nolint
}
if len(m2.Attributes) != len(m.Attributes) {
panic("attributes length missmatch") // nolint
}
return 1
}
// FuzzType is go-fuzz endpoint for message type.
func FuzzType(data []byte) int {
t := MessageType{}
vt, _ := binary.Uvarint(data)
v := uint16(vt) & 0x1fff // first 3 bits are empty
t.ReadValue(v)
v2 := t.Value()
if v != v2 {
panic("v != v2") // nolint
}
t2 := MessageType{}
t2.ReadValue(v2)
if t2 != t {
panic("t2 != t") // nolint
}
return 0
}
type attr interface {
Getter
Setter
}
type attrs []struct {
g attr
t AttrType
}
func (a attrs) pick(v byte) struct {
g attr
t AttrType
} {
idx := int(v) % len(a)
return a[idx]
}
func FuzzSetters(data []byte) int {
var (
m1 = &Message{
Raw: make([]byte, 0, 2048),
}
m2 = &Message{
Raw: make([]byte, 0, 2048),
}
m3 = &Message{
Raw: make([]byte, 0, 2048),
}
)
attributes := attrs{
{new(Realm), AttrRealm},
{new(XORMappedAddress), AttrXORMappedAddress},
{new(Nonce), AttrNonce},
{new(Software), AttrSoftware},
{new(AlternateServer), AttrAlternateServer},
{new(ErrorCodeAttribute), AttrErrorCode},
{new(UnknownAttributes), AttrUnknownAttributes},
{new(Username), AttrUsername},
{new(MappedAddress), AttrMappedAddress},
{new(Realm), AttrRealm},
}
var firstByte = byte(0)
if len(data) > 0 {
firstByte = data[0]
}
a := attributes.pick(firstByte)
value := data
if len(data) > 1 {
value = value[1:]
}
m1.WriteHeader()
m1.Add(a.t, value)
err := a.g.GetFrom(m1)
if err == ErrAttributeNotFound {
fmt.Println("unexpected 404") // nolint
panic(err) // nolint
}
if err != nil {
return 1
}
m2.WriteHeader()
if err = a.g.AddTo(m2); err != nil {
// We allow decoding some text attributes
// when their length is too big, but
// not encoding.
if !IsAttrSizeOverflow(err) {
panic(err) // nolint
}
return 1
}
m3.WriteHeader()
v, err := m2.Get(a.t)
if err != nil {
panic(err) // nolint
}
m3.Add(a.t, v)
if !m2.Equal(m3) {
fmt.Println(m2, "not equal", m3) // nolint
panic("not equal") // nolint
}
return 1
}

View file

@ -1,3 +0,0 @@
module github.com/pion/stun
go 1.12

View file

@ -1,24 +0,0 @@
#!/usr/bin/env bash
set -e
touch coverage.txt
# test fuzz inputs
go test -tags gofuzz -run TestFuzz -v .
# quick-test without -race
go test ./...
# test with "debug" tag
go test -tags debug ./...
# test concurrency
go test -race -cpu=1,2,4 -run TestClient_DoConcurrent
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic "$d"
if [[ -f profile.out ]]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done

View file

@ -1,3 +1,6 @@
// 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,
@ -21,15 +24,16 @@ type (
// 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
//
// 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 {
@ -67,7 +71,7 @@ func (m *Message) Parse(getters ...Getter) error {
func MustBuild(setters ...Setter) *Message {
m, err := Build(setters...)
if err != nil {
panic(err) // nolint
panic(err) //nolint
}
return m
}

View file

@ -1,8 +1,11 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
"crypto/md5" // #nosec
"crypto/sha1" // #nosec
import ( //nolint:gci
"crypto/md5" //nolint:gosec
"crypto/sha1" //nolint:gosec
"errors"
"fmt"
"strings"
@ -17,8 +20,7 @@ const credentialsSep = ":"
// credentials. Password, username, and realm must be SASL-prepared.
func NewLongTermIntegrity(username, realm, password string) MessageIntegrity {
k := strings.Join([]string{username, realm, password}, credentialsSep)
// #nosec
h := md5.New()
h := md5.New() //nolint:gosec
fmt.Fprint(h, k)
return MessageIntegrity(h.Sum(nil))
}

View file

@ -1,3 +1,7 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build debug
// +build debug
package stun

View file

@ -1,6 +1,5 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// 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
@ -34,19 +33,37 @@ import (
// 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 {
size int
blocksize int
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)
h.outer.Reset()
h.outer.Write(h.opad)
h.outer.Write(in[origLen:])
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])
}
@ -54,13 +71,51 @@ func (h *hmac) Write(p []byte) (n int, err error) {
return h.inner.Write(p)
}
func (h *hmac) Size() int { return h.size }
func (h *hmac) BlockSize() int { return h.blocksize }
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)
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.
@ -71,13 +126,12 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
hm := new(hmac)
hm.outer = h()
hm.inner = h()
hm.size = hm.inner.Size()
hm.blocksize = hm.inner.BlockSize()
hm.ipad = make([]byte, hm.blocksize)
hm.opad = make([]byte, hm.blocksize)
if len(key) > hm.blocksize {
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)
hm.outer.Write(key) //nolint:errcheck,gosec
key = hm.outer.Sum(nil)
}
copy(hm.ipad, key)
@ -88,7 +142,8 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
for i := range hm.opad {
hm.opad[i] ^= 0x5c
}
hm.inner.Write(hm.ipad)
hm.inner.Write(hm.ipad) //nolint:errcheck,gosec
return hm
}

View file

@ -1,29 +1,27 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package hmac
import (
"crypto/sha1"
import ( //nolint:gci
"crypto/sha1" //nolint:gosec
"crypto/sha256"
"hash"
"sync"
)
// setZeroes sets all bytes from b to zeroes.
//
// See https://github.com/golang/go/issues/5373
func setZeroes(b []byte) {
for i := range b {
b[i] = 0
}
}
func (h *hmac) resetTo(key []byte) {
h.outer.Reset()
h.inner.Reset()
setZeroes(h.ipad)
setZeroes(h.opad)
if len(key) > h.blocksize {
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)
h.outer.Write(key) //nolint:errcheck,gosec
key = h.outer.Sum(nil)
}
copy(h.ipad, key)
@ -34,10 +32,12 @@ func (h *hmac) resetTo(key []byte) {
for i := range h.opad {
h.opad[i] ^= 0x5c
}
h.inner.Write(h.ipad)
h.inner.Write(h.ipad) //nolint:errcheck,gosec
h.marshaled = false
}
var hmacSHA1Pool = &sync.Pool{
var hmacSHA1Pool = &sync.Pool{ //nolint:gochecknoglobals
New: func() interface{} {
h := New(sha1.New, make([]byte, sha1.BlockSize))
return h
@ -46,7 +46,7 @@ var hmacSHA1Pool = &sync.Pool{
// AcquireSHA1 returns new HMAC from pool.
func AcquireSHA1(key []byte) hash.Hash {
h := hmacSHA1Pool.Get().(*hmac)
h := hmacSHA1Pool.Get().(*hmac) //nolint:forcetypeassert
assertHMACSize(h, sha1.Size, sha1.BlockSize)
h.resetTo(key)
return h
@ -54,12 +54,12 @@ func AcquireSHA1(key []byte) hash.Hash {
// PutSHA1 puts h to pool.
func PutSHA1(h hash.Hash) {
hm := h.(*hmac)
hm := h.(*hmac) //nolint:forcetypeassert
assertHMACSize(hm, sha1.Size, sha1.BlockSize)
hmacSHA1Pool.Put(hm)
}
var hmacSHA256Pool = &sync.Pool{
var hmacSHA256Pool = &sync.Pool{ //nolint:gochecknoglobals
New: func() interface{} {
h := New(sha256.New, make([]byte, sha256.BlockSize))
return h
@ -68,7 +68,7 @@ var hmacSHA256Pool = &sync.Pool{
// AcquireSHA256 returns new HMAC from SHA256 pool.
func AcquireSHA256(key []byte) hash.Hash {
h := hmacSHA256Pool.Get().(*hmac)
h := hmacSHA256Pool.Get().(*hmac) //nolint:forcetypeassert
assertHMACSize(h, sha256.Size, sha256.BlockSize)
h.resetTo(key)
return h
@ -76,7 +76,7 @@ func AcquireSHA256(key []byte) hash.Hash {
// PutSHA256 puts h to SHA256 pool.
func PutSHA256(h hash.Hash) {
hm := h.(*hmac)
hm := h.(*hmac) //nolint:forcetypeassert
assertHMACSize(hm, sha256.Size, sha256.BlockSize)
hmacSHA256Pool.Put(hm)
}
@ -85,8 +85,8 @@ func PutSHA256(h hash.Hash) {
//
// Put and Acquire functions are internal functions to project, so
// checking it via such assert is optimal.
func assertHMACSize(h *hmac, size, blocksize int) {
if h.size != size || h.blocksize != blocksize {
panic("BUG: hmac size invalid") // nolint
func assertHMACSize(h *hmac, size, blocksize int) { //nolint:unparam
if h.Size() != size || h.BlockSize() != blocksize {
panic("BUG: hmac size invalid") //nolint
}
}

View file

@ -1,4 +1,7 @@
#!/bin/bash
#!/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

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -63,7 +66,7 @@ func Decode(data []byte, m *Message) error {
// 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
// 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
@ -73,6 +76,32 @@ type Message struct {
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.
@ -94,7 +123,11 @@ func (m *Message) NewTransactionID() error {
func (m *Message) String() string {
tID := base64.StdEncoding.EncodeToString(m.TransactionID[:])
return fmt.Sprintf("%s l=%d attrs=%d id=%s", m.Type, m.Length, len(m.Attributes), tID)
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.
@ -384,7 +417,6 @@ func (m *Message) Write(tBuf []byte) (int, error) {
// CloneTo clones m to b securing any further m mutations.
func (m *Message) CloneTo(b *Message) error {
// TODO(ar): implement low-level copy.
b.Raw = append(b.Raw[:0], m.Raw...)
return b.Decode()
}
@ -403,11 +435,11 @@ const (
// Common STUN message types.
var (
// Binding request message type.
BindingRequest = NewType(MethodBinding, ClassRequest)
BindingRequest = NewType(MethodBinding, ClassRequest) //nolint:gochecknoglobals
// Binding success response message type
BindingSuccess = NewType(MethodBinding, ClassSuccessResponse)
BindingSuccess = NewType(MethodBinding, ClassSuccessResponse) //nolint:gochecknoglobals
// Binding error response message type.
BindingError = NewType(MethodBinding, ClassErrorResponse)
BindingError = NewType(MethodBinding, ClassErrorResponse) //nolint:gochecknoglobals
)
func (c MessageClass) String() string {
@ -421,7 +453,7 @@ func (c MessageClass) String() string {
case ClassErrorResponse:
return "error response"
default:
panic("unknown message class") // nolint: never happens unless wrongly casted
panic("unknown message class") //nolint
}
}
@ -446,23 +478,25 @@ const (
MethodConnectionAttempt Method = 0x000c
)
var methodName = map[Method]string{
MethodBinding: "Binding",
MethodAllocate: "Allocate",
MethodRefresh: "Refresh",
MethodSend: "Send",
MethodData: "Data",
MethodCreatePermission: "CreatePermission",
MethodChannelBind: "ChannelBind",
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",
// RFC 6062.
MethodConnect: "Connect",
MethodConnectionBind: "ConnectionBind",
MethodConnectionAttempt: "ConnectionAttempt",
}
}
func (m Method) String() string {
s, ok := methodName[m]
s, ok := methodName()[m]
if !ok {
// Falling back to hex representation.
s = fmt.Sprintf("0x%x", uint16(m))

View file

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

View file

@ -1,3 +1,6 @@
// 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
@ -17,12 +20,12 @@ import (
)
// bin is shorthand to binary.BigEndian.
var bin = 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
panic(err) //nolint
}
return n
}
@ -30,7 +33,7 @@ func readFullOrPanic(r io.Reader, v []byte) int {
func writeOrPanic(w io.Writer, v []byte) int {
n, err := w.Write(v)
if err != nil {
panic(err) // nolint
panic(err) //nolint
}
return n
}
@ -48,4 +51,4 @@ func (transactionIDSetter) AddTo(m *Message) error {
}
// TransactionID is Setter for m.TransactionID.
var TransactionID Setter = transactionIDSetter{}
var TransactionID Setter = transactionIDSetter{} //nolint:gochecknoglobals

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
// NewUsername returns Username with provided value.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import "errors"

View 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
}

View file

@ -1,62 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package stun
import (
"runtime"
"unsafe"
)
// #nosec
const wordSize = int(unsafe.Sizeof(uintptr(0)))
var supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
//
// #nosec
func fastXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
for i := n - n%wordSize; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd.
func xorBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastXORBytes(dst, a, b)
}
return safeXORBytes(dst, a, b)
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package stun
import (
@ -6,6 +9,8 @@ import (
"io"
"net"
"strconv"
"github.com/pion/transport/v2/utils/xor"
)
const (
@ -66,7 +71,7 @@ func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error {
bin.PutUint32(xorValue[0:4], magicCookie)
bin.PutUint16(value[0:2], family)
bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
xorBytes(value[4:4+len(ip)], ip, xorValue)
xor.XorBytes(value[4:4+len(ip)], ip, xorValue)
m.Add(t, value[:4+len(ip)])
return nil
}
@ -115,7 +120,7 @@ func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
xorValue := make([]byte, 4+TransactionIDSize)
bin.PutUint32(xorValue[0:4], magicCookie)
copy(xorValue[4:], m.TransactionID[:])
xorBytes(a.IP, v[4:], xorValue)
xor.XorBytes(a.IP, v[4:], xorValue)
return nil
}
@ -126,20 +131,20 @@ func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
//
// 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
// // ...
// 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.
// 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)
}