summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/stun
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-11-29 01:46:27 +0100
committerkali kaneko (leap communications) <kali@leap.se>2021-11-29 18:14:16 +0100
commit18f52af5be3a9a0c73811706108f790d65ee9c67 (patch)
treee13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com/pion/stun
parentebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff)
[pkg] update vendor
Diffstat (limited to 'vendor/github.com/pion/stun')
-rw-r--r--vendor/github.com/pion/stun/.codecov.yml9
-rw-r--r--vendor/github.com/pion/stun/.gitignore17
-rw-r--r--vendor/github.com/pion/stun/.golangci.yml93
-rw-r--r--vendor/github.com/pion/stun/.goreleaser.yml39
-rw-r--r--vendor/github.com/pion/stun/.travis.yml135
-rw-r--r--vendor/github.com/pion/stun/AUTHORS10
-rw-r--r--vendor/github.com/pion/stun/Dockerfile5
-rw-r--r--vendor/github.com/pion/stun/LICENSE.md7
-rw-r--r--vendor/github.com/pion/stun/Makefile61
-rw-r--r--vendor/github.com/pion/stun/README.md184
-rw-r--r--vendor/github.com/pion/stun/addr.go134
-rw-r--r--vendor/github.com/pion/stun/agent.go228
-rw-r--r--vendor/github.com/pion/stun/appveyor.yml22
-rw-r--r--vendor/github.com/pion/stun/attributes.go226
-rw-r--r--vendor/github.com/pion/stun/attributes_debug.go33
-rw-r--r--vendor/github.com/pion/stun/checks.go45
-rw-r--r--vendor/github.com/pion/stun/checks_debug.go61
-rw-r--r--vendor/github.com/pion/stun/client.go631
-rw-r--r--vendor/github.com/pion/stun/errorcode.go158
-rw-r--r--vendor/github.com/pion/stun/errors.go62
-rw-r--r--vendor/github.com/pion/stun/fingerprint.go67
-rw-r--r--vendor/github.com/pion/stun/fingerprint_debug.go18
-rw-r--r--vendor/github.com/pion/stun/fuzz.go140
-rw-r--r--vendor/github.com/pion/stun/go.mod3
-rw-r--r--vendor/github.com/pion/stun/go.sum0
-rw-r--r--vendor/github.com/pion/stun/go.test.sh24
-rw-r--r--vendor/github.com/pion/stun/helpers.go105
-rw-r--r--vendor/github.com/pion/stun/integrity.go124
-rw-r--r--vendor/github.com/pion/stun/integrity_debug.go18
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/hmac.go101
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/pool.go92
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/vendor.sh4
-rw-r--r--vendor/github.com/pion/stun/message.go588
-rw-r--r--vendor/github.com/pion/stun/renovate.json15
-rw-r--r--vendor/github.com/pion/stun/stun.go51
-rw-r--r--vendor/github.com/pion/stun/textattrs.go129
-rw-r--r--vendor/github.com/pion/stun/uattrs.go63
-rw-r--r--vendor/github.com/pion/stun/xor.go62
-rw-r--r--vendor/github.com/pion/stun/xoraddr.go145
39 files changed, 3909 insertions, 0 deletions
diff --git a/vendor/github.com/pion/stun/.codecov.yml b/vendor/github.com/pion/stun/.codecov.yml
new file mode 100644
index 0000000..7fa67fe
--- /dev/null
+++ b/vendor/github.com/pion/stun/.codecov.yml
@@ -0,0 +1,9 @@
+coverage:
+ status:
+ patch: off
+ project:
+ default:
+ # basic
+ target: 98
+ threshold: null
+ base: auto
diff --git a/vendor/github.com/pion/stun/.gitignore b/vendor/github.com/pion/stun/.gitignore
new file mode 100644
index 0000000..b9420d9
--- /dev/null
+++ b/vendor/github.com/pion/stun/.gitignore
@@ -0,0 +1,17 @@
+*-fuzz.zip
+.idea
+benchmark.*.write
+*.test
+*.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
diff --git a/vendor/github.com/pion/stun/.golangci.yml b/vendor/github.com/pion/stun/.golangci.yml
new file mode 100644
index 0000000..40ca69c
--- /dev/null
+++ b/vendor/github.com/pion/stun/.golangci.yml
@@ -0,0 +1,93 @@
+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
+
+linters:
+ enable-all: true
+ disable:
+ - funlen
+ - gochecknoglobals
+ - godox
+ - prealloc
+ - scopelint
+
+run:
+ skip-dirs:
+ - e2e
+ - fuzz
+ - testdata
+ - api
diff --git a/vendor/github.com/pion/stun/.goreleaser.yml b/vendor/github.com/pion/stun/.goreleaser.yml
new file mode 100644
index 0000000..d72bde6
--- /dev/null
+++ b/vendor/github.com/pion/stun/.goreleaser.yml
@@ -0,0 +1,39 @@
+before:
+ hooks:
+ - go mod tidy
+
+archives:
+- replacements:
+ darwin: Darwin
+ linux: Linux
+ windows: Windows
+ 386: i386
+ amd64: x86_64
+
+checksum:
+ name_template: 'checksums.txt'
+
+snapshot:
+ name_template: "{{ .Tag }}-next"
+
+changelog:
+ sort: asc
+ filters:
+ exclude:
+ - '^docs:'
+ - '^test:'
+
+builds:
+ - binary: stun-not-behavior
+ id: stun-not-behavior
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./cmd/stun-nat-behavior
diff --git a/vendor/github.com/pion/stun/.travis.yml b/vendor/github.com/pion/stun/.travis.yml
new file mode 100644
index 0000000..e464e84
--- /dev/null
+++ b/vendor/github.com/pion/stun/.travis.yml
@@ -0,0 +1,135 @@
+#
+# 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
diff --git a/vendor/github.com/pion/stun/AUTHORS b/vendor/github.com/pion/stun/AUTHORS
new file mode 100644
index 0000000..717af8f
--- /dev/null
+++ b/vendor/github.com/pion/stun/AUTHORS
@@ -0,0 +1,10 @@
+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
diff --git a/vendor/github.com/pion/stun/Dockerfile b/vendor/github.com/pion/stun/Dockerfile
new file mode 100644
index 0000000..429239a
--- /dev/null
+++ b/vendor/github.com/pion/stun/Dockerfile
@@ -0,0 +1,5 @@
+FROM golang:1.14
+
+COPY . /go/src/github.com/pion/stun
+
+RUN go test github.com/pion/stun
diff --git a/vendor/github.com/pion/stun/LICENSE.md b/vendor/github.com/pion/stun/LICENSE.md
new file mode 100644
index 0000000..5cc9cbd
--- /dev/null
+++ b/vendor/github.com/pion/stun/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright 2018 Pion LLC
+
+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.
diff --git a/vendor/github.com/pion/stun/Makefile b/vendor/github.com/pion/stun/Makefile
new file mode 100644
index 0000000..43de8d0
--- /dev/null
+++ b/vendor/github.com/pion/stun/Makefile
@@ -0,0 +1,61 @@
+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"
+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"
+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-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
+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:
diff --git a/vendor/github.com/pion/stun/README.md b/vendor/github.com/pion/stun/README.md
new file mode 100644
index 0000000..6dd50d0
--- /dev/null
+++ b/vendor/github.com/pion/stun/README.md
@@ -0,0 +1,184 @@
+<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://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>
+ <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)]
+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() {
+ // Creating a "connection" to STUN server.
+ c, err := stun.Dial("udp", "stun.l.google.com:19302")
+ 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)
+ }
+}
+```
+
+## 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)
+
+# 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
+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.16.5
+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
+```
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/stun/addr.go b/vendor/github.com/pion/stun/addr.go
new file mode 100644
index 0000000..c4d9653
--- /dev/null
+++ b/vendor/github.com/pion/stun/addr.go
@@ -0,0 +1,134 @@
+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
+}
+
+// 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.addAs(m, AttrAlternateServer)
+}
+
+// GetFrom decodes ALTERNATE-SERVER from message.
+func (s *AlternateServer) GetFrom(m *Message) error {
+ a := (*MappedAddress)(s)
+ return a.getAs(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 {
+ 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
+}
+
+func (a *MappedAddress) addAs(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.addAs(m, AttrMappedAddress)
+}
+
+// GetFrom decodes MAPPED-ADDRESS from message.
+func (a *MappedAddress) GetFrom(m *Message) error {
+ return a.getAs(m, AttrMappedAddress)
+}
+
+// AddTo adds OTHER-ADDRESS attribute to message.
+func (o *OtherAddress) AddTo(m *Message) error {
+ a := (*MappedAddress)(o)
+ return a.addAs(m, AttrOtherAddress)
+}
+
+// GetFrom decodes OTHER-ADDRESS from message.
+func (o *OtherAddress) GetFrom(m *Message) error {
+ a := (*MappedAddress)(o)
+ return a.getAs(m, AttrOtherAddress)
+}
+
+func (o OtherAddress) String() string {
+ return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port))
+}
diff --git a/vendor/github.com/pion/stun/agent.go b/vendor/github.com/pion/stun/agent.go
new file mode 100644
index 0000000..6a8a473
--- /dev/null
+++ b/vendor/github.com/pion/stun/agent.go
@@ -0,0 +1,228 @@
+package stun
+
+import (
+ "errors"
+ "sync"
+ "time"
+)
+
+// NoopHandler just discards any event.
+var NoopHandler Handler = 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
diff --git a/vendor/github.com/pion/stun/appveyor.yml b/vendor/github.com/pion/stun/appveyor.yml
new file mode 100644
index 0000000..664099d
--- /dev/null
+++ b/vendor/github.com/pion/stun/appveyor.yml
@@ -0,0 +1,22 @@
+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 .
diff --git a/vendor/github.com/pion/stun/attributes.go b/vendor/github.com/pion/stun/attributes.go
new file mode 100644
index 0000000..7238234
--- /dev/null
+++ b/vendor/github.com/pion/stun/attributes.go
@@ -0,0 +1,226 @@
+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 (
+ AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS
+ AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST
+)
+
+// 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
+)
+
+// 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 (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 {
+ return AttrXORMappedAddress // new: 0x0020
+ }
+ return AttrType(val)
+}
diff --git a/vendor/github.com/pion/stun/attributes_debug.go b/vendor/github.com/pion/stun/attributes_debug.go
new file mode 100644
index 0000000..7bf09af
--- /dev/null
+++ b/vendor/github.com/pion/stun/attributes_debug.go
@@ -0,0 +1,33 @@
+// +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,
+ )
+}
diff --git a/vendor/github.com/pion/stun/checks.go b/vendor/github.com/pion/stun/checks.go
new file mode 100644
index 0000000..a760997
--- /dev/null
+++ b/vendor/github.com/pion/stun/checks.go
@@ -0,0 +1,45 @@
+// +build !debug
+
+package stun
+
+import "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 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 err == ErrAttributeSizeOverflow
+}
diff --git a/vendor/github.com/pion/stun/checks_debug.go b/vendor/github.com/pion/stun/checks_debug.go
new file mode 100644
index 0000000..955f555
--- /dev/null
+++ b/vendor/github.com/pion/stun/checks_debug.go
@@ -0,0 +1,61 @@
+// +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
+}
diff --git a/vendor/github.com/pion/stun/client.go b/vendor/github.com/pion/stun/client.go
new file mode 100644
index 0000000..62a0b6e
--- /dev/null
+++ b/vendor/github.com/pion/stun/client.go
@@ -0,0 +1,631 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// 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)
+}
+
+// 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.
+var WithNoConnClose ClientOption = 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 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{
+ New: func() interface{} {
+ return &clientTransaction{
+ raw: make([]byte, 1500),
+ }
+ },
+}
+
+func acquireClientTransaction() *clientTransaction {
+ return clientTransactionPool.Get().(*clientTransaction)
+}
+
+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() }
+
+var systemClock = 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.
+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.
+type CloseErr struct {
+ AgentErr error
+ ConnectionErr error
+}
+
+func sprintErr(err error) string {
+ if err == nil {
+ return "<nil>"
+ }
+ 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); pErr == ErrAgentClosed {
+ return
+ }
+ }
+ }
+}
+
+func closedOrPanic(err error) {
+ if err == nil || 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{
+ 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)
+ 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{
+ 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 && 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)
+ 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
+}
diff --git a/vendor/github.com/pion/stun/errorcode.go b/vendor/github.com/pion/stun/errorcode.go
new file mode 100644
index 0000000..8095048
--- /dev/null
+++ b/vendor/github.com/pion/stun/errorcode.go
@@ -0,0 +1,158 @@
+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, 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
+)
+
+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"),
+}
diff --git a/vendor/github.com/pion/stun/errors.go b/vendor/github.com/pion/stun/errors.go
new file mode 100644
index 0000000..029c9e4
--- /dev/null
+++ b/vendor/github.com/pion/stun/errors.go
@@ -0,0 +1,62 @@
+package stun
+
+import "errors"
+
+// DecodeErr records an error and place when it is occurred.
+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,
+ }
+}
+
+// TODO(ar): rewrite errors to be more precise.
+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")
diff --git a/vendor/github.com/pion/stun/fingerprint.go b/vendor/github.com/pion/stun/fingerprint.go
new file mode 100644
index 0000000..aef80a2
--- /dev/null
+++ b/vendor/github.com/pion/stun/fingerprint.go
@@ -0,0 +1,67 @@
+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
+
+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)
+}
diff --git a/vendor/github.com/pion/stun/fingerprint_debug.go b/vendor/github.com/pion/stun/fingerprint_debug.go
new file mode 100644
index 0000000..6da074c
--- /dev/null
+++ b/vendor/github.com/pion/stun/fingerprint_debug.go
@@ -0,0 +1,18 @@
+// +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,
+ )
+}
diff --git a/vendor/github.com/pion/stun/fuzz.go b/vendor/github.com/pion/stun/fuzz.go
new file mode 100644
index 0000000..debfa8d
--- /dev/null
+++ b/vendor/github.com/pion/stun/fuzz.go
@@ -0,0 +1,140 @@
+// +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
+}
diff --git a/vendor/github.com/pion/stun/go.mod b/vendor/github.com/pion/stun/go.mod
new file mode 100644
index 0000000..72b5671
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.mod
@@ -0,0 +1,3 @@
+module github.com/pion/stun
+
+go 1.12
diff --git a/vendor/github.com/pion/stun/go.sum b/vendor/github.com/pion/stun/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.sum
diff --git a/vendor/github.com/pion/stun/go.test.sh b/vendor/github.com/pion/stun/go.test.sh
new file mode 100644
index 0000000..25234e4
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.test.sh
@@ -0,0 +1,24 @@
+#!/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
diff --git a/vendor/github.com/pion/stun/helpers.go b/vendor/github.com/pion/stun/helpers.go
new file mode 100644
index 0000000..158a51d
--- /dev/null
+++ b/vendor/github.com/pion/stun/helpers.go
@@ -0,0 +1,105 @@
+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
+}
diff --git a/vendor/github.com/pion/stun/integrity.go b/vendor/github.com/pion/stun/integrity.go
new file mode 100644
index 0000000..39b0d50
--- /dev/null
+++ b/vendor/github.com/pion/stun/integrity.go
@@ -0,0 +1,124 @@
+package stun
+
+import (
+ "crypto/md5" // #nosec
+ "crypto/sha1" // #nosec
+ "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)
+ // #nosec
+ h := md5.New()
+ 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)
+}
diff --git a/vendor/github.com/pion/stun/integrity_debug.go b/vendor/github.com/pion/stun/integrity_debug.go
new file mode 100644
index 0000000..6b8a303
--- /dev/null
+++ b/vendor/github.com/pion/stun/integrity_debug.go
@@ -0,0 +1,18 @@
+// +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,
+ )
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/hmac.go b/vendor/github.com/pion/stun/internal/hmac/hmac.go
new file mode 100644
index 0000000..801ece6
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/hmac.go
@@ -0,0 +1,101 @@
+// 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.
+
+/*
+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))
+
+type hmac struct {
+ size int
+ blocksize int
+ opad, ipad []byte
+ outer, inner hash.Hash
+}
+
+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:])
+ 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.size }
+
+func (h *hmac) BlockSize() int { return h.blocksize }
+
+func (h *hmac) Reset() {
+ h.inner.Reset()
+ h.inner.Write(h.ipad)
+}
+
+// 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()
+ 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 {
+ // If key is too big, hash it.
+ hm.outer.Write(key)
+ 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)
+ 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
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/pool.go b/vendor/github.com/pion/stun/internal/hmac/pool.go
new file mode 100644
index 0000000..4db61e5
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/pool.go
@@ -0,0 +1,92 @@
+package hmac
+
+import (
+ "crypto/sha1"
+ "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 {
+ // If key is too big, hash it.
+ h.outer.Write(key)
+ 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)
+}
+
+var hmacSHA1Pool = &sync.Pool{
+ 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)
+ assertHMACSize(h, sha1.Size, sha1.BlockSize)
+ h.resetTo(key)
+ return h
+}
+
+// PutSHA1 puts h to pool.
+func PutSHA1(h hash.Hash) {
+ hm := h.(*hmac)
+ assertHMACSize(hm, sha1.Size, sha1.BlockSize)
+ hmacSHA1Pool.Put(hm)
+}
+
+var hmacSHA256Pool = &sync.Pool{
+ 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)
+ 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)
+ 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) {
+ if h.size != size || h.blocksize != blocksize {
+ panic("BUG: hmac size invalid") // nolint
+ }
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/vendor.sh b/vendor/github.com/pion/stun/internal/hmac/vendor.sh
new file mode 100644
index 0000000..83a2b32
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/vendor.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cp -v $GOROOT/src/crypto/hmac/{hmac,hmac_test}.go .
+git diff {hmac,hmac_test}.go
+
diff --git a/vendor/github.com/pion/stun/message.go b/vendor/github.com/pion/stun/message.go
new file mode 100644
index 0000000..3819235
--- /dev/null
+++ b/vendor/github.com/pion/stun/message.go
@@ -0,0 +1,588 @@
+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
+}
+
+// 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[:])
+ return fmt.Sprintf("%s l=%d attrs=%d id=%s", m.Type, m.Length, len(m.Attributes), tID)
+}
+
+// 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 {
+ // TODO(ar): implement low-level copy.
+ 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)
+ // Binding success response message type
+ BindingSuccess = NewType(MethodBinding, ClassSuccessResponse)
+ // Binding error response message type.
+ BindingError = NewType(MethodBinding, ClassErrorResponse)
+)
+
+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: never happens unless wrongly casted
+ }
+}
+
+// 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
+)
+
+var methodName = 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
+}
diff --git a/vendor/github.com/pion/stun/renovate.json b/vendor/github.com/pion/stun/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/stun/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/stun/stun.go b/vendor/github.com/pion/stun/stun.go
new file mode 100644
index 0000000..9f804d2
--- /dev/null
+++ b/vendor/github.com/pion/stun/stun.go
@@ -0,0 +1,51 @@
+// 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
+
+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{}
diff --git a/vendor/github.com/pion/stun/textattrs.go b/vendor/github.com/pion/stun/textattrs.go
new file mode 100644
index 0000000..efdfbd9
--- /dev/null
+++ b/vendor/github.com/pion/stun/textattrs.go
@@ -0,0 +1,129 @@
+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
+}
diff --git a/vendor/github.com/pion/stun/uattrs.go b/vendor/github.com/pion/stun/uattrs.go
new file mode 100644
index 0000000..238d32d
--- /dev/null
+++ b/vendor/github.com/pion/stun/uattrs.go
@@ -0,0 +1,63 @@
+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
+}
diff --git a/vendor/github.com/pion/stun/xor.go b/vendor/github.com/pion/stun/xor.go
new file mode 100644
index 0000000..34365eb
--- /dev/null
+++ b/vendor/github.com/pion/stun/xor.go
@@ -0,0 +1,62 @@
+// 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)
+}
diff --git a/vendor/github.com/pion/stun/xoraddr.go b/vendor/github.com/pion/stun/xoraddr.go
new file mode 100644
index 0000000..23f7777
--- /dev/null
+++ b/vendor/github.com/pion/stun/xoraddr.go
@@ -0,0 +1,145 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+)
+
+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))
+ 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[:])
+ 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)
+}