diff --git a/.github/assert-contributors.sh b/.github/assert-contributors.sh new file mode 100644 index 0000000..b3baa57 --- /dev/null +++ b/.github/assert-contributors.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -e + +# Unshallow the repo, this check doesn't work with this enabled +# https://github.com/travis-ci/travis-ci/issues/3412 +if [ -f $(git rev-parse --git-dir)/shallow ]; then + git fetch --unshallow || true +fi + +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +CONTRIBUTORS=() +EXCLUDED_CONTIBUTORS=('John R. Bradley') +MISSING_CONTIBUTORS=() + +shouldBeIncluded () { + for i in "${EXCLUDED_CONTIBUTORS[@]}" + do + if [ "$i" == "$1" ] ; then + return 1 + fi + done + return 0 +} + + +IFS=$'\n' #Only split on newline +for contributor in $(git log --format='%aN' | sort -u) +do + if shouldBeIncluded $contributor; then + if ! grep -q "$contributor" "$SCRIPT_PATH/../README.md"; then + MISSING_CONTIBUTORS+=("$contributor") + fi + fi +done +unset IFS + +if [ ${#MISSING_CONTIBUTORS[@]} -ne 0 ]; then + echo "Please add the following contributors to the README" + for i in "${MISSING_CONTIBUTORS[@]}" + do + echo "$i" + done + exit 1 +fi diff --git a/.github/lint-commit-message.sh b/.github/lint-commit-message.sh new file mode 100644 index 0000000..df2ea30 --- /dev/null +++ b/.github/lint-commit-message.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -e + +display_commit_message_error() { +cat << EndOfMessage +$1 + +------------------------------------------------- +The preceding commit message is invalid +it failed '$2' of the following checks + +* Separate subject from body with a blank line +* Limit the subject line to 50 characters +* Capitalize the subject line +* Do not end the subject line with a period +* Wrap the body at 72 characters +EndOfMessage + + exit 1 +} + +lint_commit_message() { + if [[ "$(echo "$1" | awk 'NR == 2 {print $1;}' | wc -c)" -ne 1 ]]; then + display_commit_message_error "$1" 'Separate subject from body with a blank line' + fi + + if [[ "$(echo "$1" | head -n1 | wc -m)" -gt 50 ]]; then + display_commit_message_error "$1" 'Limit the subject line to 50 characters' + fi + + if [[ ! $1 =~ ^[A-Z] ]]; then + display_commit_message_error "$1" 'Capitalize the subject line' + fi + + if [[ "$(echo "$1" | awk 'NR == 1 {print substr($0,length($0),1)}')" == "." ]]; then + display_commit_message_error "$1" 'Do not end the subject line with a period' + fi + + if [[ "$(echo "$1" | awk '{print length}' | sort -nr | head -1)" -gt 72 ]]; then + display_commit_message_error "$1" 'Wrap the body at 72 characters' + fi +} + +if [ "$#" -eq 1 ]; then + if [ ! -f "$1" ]; then + echo "$0 was passed one argument, but was not a valid file" + exit 1 + fi + lint_commit_message "$(sed -n '/# Please enter the commit message for your changes. Lines starting/q;p' "$1")" +else + # TRAVIS_COMMIT_RANGE is empty for initial branch commit + if [[ "${TRAVIS_COMMIT_RANGE}" != *"..."* ]]; then + parent=$(git log -n 1 --format="%P" ${TRAVIS_COMMIT_RANGE}) + TRAVIS_COMMIT_RANGE="${TRAVIS_COMMIT_RANGE}...$parent" + fi + + for commit in $(git rev-list ${TRAVIS_COMMIT_RANGE}); do + lint_commit_message "$(git log --format="%B" -n 1 $commit)" + done +fi diff --git a/.github/lint-disallowed-functions-in-library.sh b/.github/lint-disallowed-functions-in-library.sh new file mode 100644 index 0000000..f316c18 --- /dev/null +++ b/.github/lint-disallowed-functions-in-library.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +# Disallow usages of functions that cause the program to exit in the library code +SCRIPT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +EXCLUDE_DIRECTORIES="--exclude-dir=examples --exclude-dir=.git --exclude-dir=.github " +DISALLOWED_FUNCTIONS=('os.Exit(' 'panic(' 'Fatal(' 'Fatalf(' 'Fatalln(') + + +for disallowedFunction in "${DISALLOWED_FUNCTIONS[@]}" +do + if grep -R $EXCLUDE_DIRECTORIES -e "$disallowedFunction" "$SCRIPT_PATH/.." | grep -v -e '_test.go' -e 'nolint'; then + echo "$disallowedFunction may only be used in example code" + exit 1 + fi +done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7dd424e --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +### JetBrains IDE ### +##################### +.idea/ + +### Emacs Temporary Files ### +############################# +*~ + +### Folders ### +############### +bin/ +vendor/ +node_modules/ + +### Files ### +############# +tags +cover.out +*.sw[poe] +*.wasm diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..afb7ff5 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,17 @@ +linters-settings: + govet: + check-shadowing: true + misspell: + locale: US + +linters: + enable-all: true + disable: + - lll + - maligned + - gochecknoglobals + +issues: + exclude-use-default: false + max-per-linter: 0 + max-same-issues: 50 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..515a03c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: go + +go: + - "1.x" # use the latest Go release + +env: + - GO111MODULE=on + +before_script: + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0 + - go get github.com/mattn/goveralls + +script: + - golangci-lint run ./... +# - rm -rf examples # Remove examples, no test coverage for them + - go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./... + - goveralls -coverprofile=cover.out -service=travis-ci + - bash .github/assert-contributors.sh + - bash .github/lint-disallowed-functions-in-library.sh + - bash .github/lint-commit-message.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +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/README.md b/README.md new file mode 100644 index 0000000..7c67e8e --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +

+
+ Pion ICE +
+

+

A Go implementation of ICE

+

+ Pion transport + Slack Widget + Waffle board +
+ Build Status + GoDoc + Coverage Status + Go Report Card + License: MIT +

+
+ +### Roadmap +The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pions/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/pions/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: + +* [John Bradley](https://github.com/kc5nra) - *Original Author* +* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* +* [Michael MacDonald](https://github.com/mjmac) - *Original Author* +* [Michiel De Backker](https://github.com/backkem) - *Original Author* +* [Konstantin Itskov](https://github.com/trivigy) - *Original Author* +* [Luke Curley](https://github.com/kixelated) + +### License +MIT License - see [LICENSE](LICENSE) for full text diff --git a/agent.go b/agent.go index 0139b15..efc4b34 100644 --- a/agent.go +++ b/agent.go @@ -15,7 +15,6 @@ import ( "github.com/pions/logging" "github.com/pions/stun" "github.com/pions/transport/packetio" - "github.com/pions/webrtc/internal/util" ) const ( @@ -139,8 +138,8 @@ func NewAgent(config *AgentConfig) (*Agent, error) { localCandidates: make(map[NetworkType][]*Candidate), remoteCandidates: make(map[NetworkType][]*Candidate), - localUfrag: util.RandSeq(16), - localPwd: util.RandSeq(32), + localUfrag: randSeq(16), + localPwd: randSeq(32), taskChan: make(chan task), onConnected: make(chan struct{}), buffer: packetio.NewBuffer(), @@ -294,7 +293,7 @@ func allocateUDP(network string, url *URL) (*net.UDPAddr, *stun.XorAddress, erro // TODO Do we want the timeout to be configurable? client, err := stun.NewClient(network, fmt.Sprintf("%s:%d", url.Host, url.Port), time.Second*5) if err != nil { - return nil, nil, util.FlattenErrs([]error{errors.New("failed to create STUN client"), err}) + return nil, nil, flattenErrs([]error{errors.New("failed to create STUN client"), err}) } localAddr, ok := client.LocalAddr().(*net.UDPAddr) if !ok { @@ -303,11 +302,11 @@ func allocateUDP(network string, url *URL) (*net.UDPAddr, *stun.XorAddress, erro resp, err := client.Request() if err != nil { - return nil, nil, util.FlattenErrs([]error{errors.New("failed to make STUN request"), err}) + return nil, nil, flattenErrs([]error{errors.New("failed to make STUN request"), err}) } if err = client.Close(); err != nil { - return nil, nil, util.FlattenErrs([]error{errors.New("failed to close STUN client"), err}) + return nil, nil, flattenErrs([]error{errors.New("failed to close STUN client"), err}) } attr, ok := resp.GetOneAttribute(stun.AttrXORMappedAddress) @@ -317,7 +316,7 @@ func allocateUDP(network string, url *URL) (*net.UDPAddr, *stun.XorAddress, erro var addr stun.XorAddress if err = addr.Unpack(resp, attr); err != nil { - return nil, nil, util.FlattenErrs([]error{errors.New("failed to unpack STUN XorAddress response"), err}) + return nil, nil, flattenErrs([]error{errors.New("failed to unpack STUN XorAddress response"), err}) } return localAddr, &addr, nil @@ -732,7 +731,7 @@ func (a *Agent) handleNewPeerReflexiveCandidate(local *Candidate, remote net.Add ) if err != nil { - return util.FlattenErrs([]error{fmt.Errorf("failed to create peer-reflexive candidate: %v", remote), err}) + return flattenErrs([]error{fmt.Errorf("failed to create peer-reflexive candidate: %v", remote), err}) } // Add pflxCandidate to the remote candidate list diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..35cdfca --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/pions/ice + +go 1.12 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/util.go b/util.go index 9de782a..0d565f2 100644 --- a/util.go +++ b/util.go @@ -1,8 +1,12 @@ package ice import ( + "fmt" + "math/rand" "net" + "strings" "sync/atomic" + "time" ) func localInterfaces(networkTypes []NetworkType) (ips []net.IP) { @@ -95,3 +99,31 @@ func isZeros(ip net.IP) bool { } return true } + +// RandSeq generates a random alpha numeric sequence of the requested length +func randSeq(n int) string { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, n) + for i := range b { + b[i] = letters[r.Intn(len(letters))] + } + return string(b) +} + +// flattenErrs flattens multiple errors into one +func flattenErrs(errs []error) error { + var errstrings []string + + for _, err := range errs { + if err != nil { + errstrings = append(errstrings, err.Error()) + } + } + + if len(errstrings) == 0 { + return nil + } + + return fmt.Errorf(strings.Join(errstrings, "\n")) +} diff --git a/util_test.go b/util_test.go new file mode 100644 index 0000000..83f1ca9 --- /dev/null +++ b/util_test.go @@ -0,0 +1,17 @@ +package ice + +import ( + "regexp" + "testing" +) + +func TestRandSeq(t *testing.T) { + if len(randSeq(10)) != 10 { + t.Errorf("randSeq return invalid length") + } + + var isLetter = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString + if !isLetter(randSeq(10)) { + t.Errorf("randSeq should be AlphaNumeric only") + } +}