pax_global_header00006660000000000000000000000064140351400400014501gustar00rootroot0000000000000052 comment=4e5e57c070e184437e43194991bbd40a3c08c90b go-stun-0.1.3/000077500000000000000000000000001403514004000130765ustar00rootroot00000000000000go-stun-0.1.3/.github/000077500000000000000000000000001403514004000144365ustar00rootroot00000000000000go-stun-0.1.3/.github/workflows/000077500000000000000000000000001403514004000164735ustar00rootroot00000000000000go-stun-0.1.3/.github/workflows/go.yml000066400000000000000000000012471403514004000176270ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ^1.13 id: go - name: Check out code into the Go module directory uses: actions/checkout@v2 - name: Get dependencies run: | go get -v -t -d ./... if [ -f Gopkg.toml ]; then curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure fi - name: Build run: go build -v ./... - name: Test run: go test -v ./... go-stun-0.1.3/.gitignore000066400000000000000000000004531403514004000150700ustar00rootroot00000000000000# Temporary files for running example go-stun # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe go-stun-0.1.3/.travis.yml000066400000000000000000000003601403514004000152060ustar00rootroot00000000000000language: go arch: - amd64 - ppc64le go: - 1.14.x - tip script: - go build ./... - go test -v ./... - go test -race -coverprofile=coverage.txt -covermode=atomic ./... after_success: - bash <(curl -s https://codecov.io/bash) go-stun-0.1.3/LICENSE000066400000000000000000000240411403514004000141040ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. go-stun-0.1.3/README.md000066400000000000000000000033331403514004000143570ustar00rootroot00000000000000go-stun ======= [![Build Status](https://travis-ci.org/ccding/go-stun.svg?branch=master)](https://travis-ci.org/ccding/go-stun) [![License](https://img.shields.io/badge/License-Apache%202.0-red.svg)](https://opensource.org/licenses/Apache-2.0) [![GoDoc](https://godoc.org/github.com/ccding/go-stun?status.svg)](http://godoc.org/github.com/ccding/go-stun/stun) [![Go Report Card](https://goreportcard.com/badge/github.com/ccding/go-stun)](https://goreportcard.com/report/github.com/ccding/go-stun) go-stun is a STUN (RFC 3489, 5389) client implementation in golang (a.k.a. UDP hole punching). [RFC 3489](https://tools.ietf.org/html/rfc3489): STUN - Simple Traversal of User Datagram Protocol (UDP) Through Network Address Translators (NATs) [RFC 5389](https://tools.ietf.org/html/rfc5389): Session Traversal Utilities for NAT (STUN) ### Use the Command Line Tool Simply run these commands (if you have installed golang and set `$GOPATH`) ``` go get github.com/ccding/go-stun go-stun ``` or clone this repo and run these commands ``` go build ./go-stun ``` You will get the output like ``` NAT Type: Full cone NAT External IP Family: 1 External IP: 166.111.4.100 External Port: 23009 ``` You can use `-s` flag to use another STUN server, and use `-v` to work on verbose mode. ```bash > ./go-stun --help Usage of ./go-stun: -s string server address (default "stun1.l.google.com:19302") -v verbose mode ``` ### Use the Library The library `github.com/ccding/go-stun/stun` is extremely easy to use -- just one line of code. ```go import "github.com/ccding/go-stun/stun" func main() { nat, host, err := stun.NewClient().Discover() } ``` More details please go to `main.go` and [GoDoc](http://godoc.org/github.com/ccding/go-stun/stun) go-stun-0.1.3/go.mod000066400000000000000000000000521403514004000142010ustar00rootroot00000000000000module github.com/ccding/go-stun go 1.14 go-stun-0.1.3/linter_config.json000066400000000000000000000005621403514004000166160ustar00rootroot00000000000000{ "Vendor": true, "DisableAll": true, "Enable": [ "vet", "safesql", "errcheck", "goconst", "goimports", "varcheck", "gas", "staticcheck", "gosimple", "lll", "unconvert", "misspell", "unconvert" ], "Aggregate": true, "WarnUnmatchedNolint": true, "LineLength": 240, "Exclude": [ "stun/const.go" ], "Deadline": "300s", "Skip": [] } go-stun-0.1.3/main.go000066400000000000000000000033101403514004000143460ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "flag" "fmt" "github.com/ccding/go-stun/stun" ) func main() { var serverAddr = flag.String("s", stun.DefaultServerAddr, "STUN server address") var v = flag.Bool("v", false, "verbose mode") var vv = flag.Bool("vv", false, "double verbose mode (includes -v)") var vvv = flag.Bool("vvv", false, "triple verbose mode (includes -v and -vv)") flag.Parse() // Creates a STUN client. NewClientWithConnection can also be used if you want to handle the UDP listener by yourself. client := stun.NewClient() // The default addr (stun.DefaultServerAddr) will be used unless we call SetServerAddr. client.SetServerAddr(*serverAddr) // Non verbose mode will be used by default unless we call SetVerbose(true) or SetVVerbose(true). client.SetVerbose(*v || *vv || *vvv) client.SetVVerbose(*vv || *vvv) // Discover the NAT and return the result. nat, host, err := client.Discover() if err != nil { fmt.Println(err) return } fmt.Println("NAT Type:", nat) if host != nil { fmt.Println("External IP Family:", host.Family()) fmt.Println("External IP:", host.IP()) fmt.Println("External Port:", host.Port()) } } go-stun-0.1.3/stun/000077500000000000000000000000001403514004000140675ustar00rootroot00000000000000go-stun-0.1.3/stun/attribute.go000066400000000000000000000067611403514004000164330ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "encoding/binary" "hash/crc32" "net" ) type attribute struct { types uint16 length uint16 value []byte } func newAttribute(types uint16, value []byte) *attribute { att := new(attribute) att.types = types att.value = padding(value) att.length = uint16(len(att.value)) return att } func newFingerprintAttribute(packet *packet) *attribute { crc := crc32.ChecksumIEEE(packet.bytes()) ^ fingerprint buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, crc) return newAttribute(attributeFingerprint, buf) } func newSoftwareAttribute(name string) *attribute { return newAttribute(attributeSoftware, []byte(name)) } func newChangeReqAttribute(changeIP bool, changePort bool) *attribute { value := make([]byte, 4) if changeIP { value[3] |= 0x04 } if changePort { value[3] |= 0x02 } return newAttribute(attributeChangeRequest, value) } // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |x x x x x x x x| Family | X-Port | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | X-Address (Variable) // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Figure 6: Format of XOR-MAPPED-ADDRESS Attribute func (v *attribute) xorAddr(transID []byte) *Host { xorIP := make([]byte, 16) for i := 0; i < len(v.value)-4; i++ { xorIP[i] = v.value[i+4] ^ transID[i] } family := uint16(v.value[1]) port := binary.BigEndian.Uint16(v.value[2:4]) // Truncate if IPv4, otherwise net.IP sometimes renders it as an IPv6 address. if family == attributeFamilyIPv4 { xorIP = xorIP[:4] } x := binary.BigEndian.Uint16(transID[:2]) return &Host{family, net.IP(xorIP).String(), port ^ x} } // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |0 0 0 0 0 0 0 0| Family | Port | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | | // | Address (32 bits or 128 bits) | // | | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Figure 5: Format of MAPPED-ADDRESS Attribute func (v *attribute) rawAddr() *Host { host := new(Host) host.family = uint16(v.value[1]) host.port = binary.BigEndian.Uint16(v.value[2:4]) // Truncate if IPv4, otherwise net.IP sometimes renders it as an IPv6 address. if host.family == attributeFamilyIPv4 { v.value = v.value[:8] } host.ip = net.IP(v.value[4:]).String() return host } go-stun-0.1.3/stun/client.go000066400000000000000000000070551403514004000157030ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "errors" "net" "strconv" ) // Client is a STUN client, which can be set STUN server address and is used // to discover NAT type. type Client struct { serverAddr string softwareName string conn net.PacketConn logger *Logger } // NewClient returns a client without network connection. The network // connection will be build when calling Discover function. func NewClient() *Client { c := new(Client) c.SetSoftwareName(DefaultSoftwareName) c.logger = NewLogger() return c } // NewClientWithConnection returns a client which uses the given connection. // Please note the connection should be acquired via net.Listen* method. func NewClientWithConnection(conn net.PacketConn) *Client { c := new(Client) c.conn = conn c.SetSoftwareName(DefaultSoftwareName) c.logger = NewLogger() return c } // SetVerbose sets the client to be in the verbose mode, which prints // information in the discover process. func (c *Client) SetVerbose(v bool) { c.logger.SetDebug(v) } // SetVVerbose sets the client to be in the double verbose mode, which prints // information and packet in the discover process. func (c *Client) SetVVerbose(v bool) { c.logger.SetInfo(v) } // SetServerHost allows user to set the STUN hostname and port. func (c *Client) SetServerHost(host string, port int) { c.serverAddr = net.JoinHostPort(host, strconv.Itoa(port)) } // SetServerAddr allows user to set the transport layer STUN server address. func (c *Client) SetServerAddr(address string) { c.serverAddr = address } // SetSoftwareName allows user to set the name of the software, which is used // for logging purpose (NOT used in the current implementation). func (c *Client) SetSoftwareName(name string) { c.softwareName = name } // Discover contacts the STUN server and gets the response of NAT type, host // for UDP punching. func (c *Client) Discover() (NATType, *Host, error) { if c.serverAddr == "" { c.SetServerAddr(DefaultServerAddr) } serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr) if err != nil { return NATError, nil, err } // Use the connection passed to the client if it is not nil, otherwise // create a connection and close it at the end. conn := c.conn if conn == nil { conn, err = net.ListenUDP("udp", nil) if err != nil { return NATError, nil, err } defer conn.Close() } return c.discover(conn, serverUDPAddr) } // Keepalive sends and receives a bind request, which ensures the mapping stays open // Only applicable when client was created with a connection. func (c *Client) Keepalive() (*Host, error) { if c.conn == nil { return nil, errors.New("no connection available") } if c.serverAddr == "" { c.SetServerAddr(DefaultServerAddr) } serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr) if err != nil { return nil, err } resp, err := c.test1(c.conn, serverUDPAddr) if err != nil { return nil, err } if resp == nil || resp.packet == nil { return nil, errors.New("failed to contact") } return resp.mappedAddr, nil } go-stun-0.1.3/stun/const.go000066400000000000000000000134551403514004000155540ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun // Default server address and client name. const ( DefaultServerAddr = "stun.ekiga.net:3478" DefaultSoftwareName = "StunClient" ) const ( magicCookie = 0x2112A442 fingerprint = 0x5354554e ) // NATType is the type of NAT described by int. type NATType int // NAT types. const ( NATError NATType = iota NATUnknown NATNone NATBlocked NATFull NATSymmetric NATRestricted NATPortRestricted SymmetricUDPFirewall // Deprecated spellings of these constants NATSymetric = NATSymmetric NATSymetricUDPFirewall = SymmetricUDPFirewall NATSymmetricUDPFirewall = SymmetricUDPFirewall ) var natStr map[NATType]string func init() { natStr = map[NATType]string{ NATError: "Test failed", NATUnknown: "Unexpected response from the STUN server", NATBlocked: "UDP is blocked", NATFull: "Full cone NAT", NATSymmetric: "Symmetric NAT", NATRestricted: "Restricted NAT", NATPortRestricted: "Port restricted NAT", NATNone: "Not behind a NAT", SymmetricUDPFirewall: "Symmetric UDP firewall", } } func (nat NATType) String() string { if s, ok := natStr[nat]; ok { return s } return "Unknown" } const ( errorTryAlternate = 300 errorBadRequest = 400 errorUnauthorized = 401 errorUnassigned402 = 402 errorForbidden = 403 errorUnknownAttribute = 420 errorAllocationMismatch = 437 errorStaleNonce = 438 errorUnassigned439 = 439 errorAddressFamilyNotSupported = 440 errorWrongCredentials = 441 errorUnsupportedTransportProtocol = 442 errorPeerAddressFamilyMismatch = 443 errorConnectionAlreadyExists = 446 errorConnectionTimeoutOrFailure = 447 errorAllocationQuotaReached = 486 errorRoleConflict = 487 errorServerError = 500 errorInsufficientCapacity = 508 ) const ( attributeFamilyIPv4 = 0x01 attributeFamilyIPV6 = 0x02 ) const ( attributeMappedAddress = 0x0001 attributeResponseAddress = 0x0002 attributeChangeRequest = 0x0003 attributeSourceAddress = 0x0004 attributeChangedAddress = 0x0005 attributeUsername = 0x0006 attributePassword = 0x0007 attributeMessageIntegrity = 0x0008 attributeErrorCode = 0x0009 attributeUnknownAttributes = 0x000a attributeReflectedFrom = 0x000b attributeChannelNumber = 0x000c attributeLifetime = 0x000d attributeBandwidth = 0x0010 attributeXorPeerAddress = 0x0012 attributeData = 0x0013 attributeRealm = 0x0014 attributeNonce = 0x0015 attributeXorRelayedAddress = 0x0016 attributeRequestedAddressFamily = 0x0017 attributeEvenPort = 0x0018 attributeRequestedTransport = 0x0019 attributeDontFragment = 0x001a attributeXorMappedAddress = 0x0020 attributeTimerVal = 0x0021 attributeReservationToken = 0x0022 attributePriority = 0x0024 attributeUseCandidate = 0x0025 attributePadding = 0x0026 attributeResponsePort = 0x0027 attributeConnectionID = 0x002a attributeXorMappedAddressExp = 0x8020 attributeSoftware = 0x8022 attributeAlternateServer = 0x8023 attributeCacheTimeout = 0x8027 attributeFingerprint = 0x8028 attributeIceControlled = 0x8029 attributeIceControlling = 0x802a attributeResponseOrigin = 0x802b attributeOtherAddress = 0x802c attributeEcnCheckStun = 0x802d attributeCiscoFlowdata = 0xc000 ) const ( typeBindingRequest = 0x0001 typeBindingResponse = 0x0101 typeBindingErrorResponse = 0x0111 typeSharedSecretRequest = 0x0002 typeSharedSecretResponse = 0x0102 typeSharedErrorResponse = 0x0112 typeAllocate = 0x0003 typeAllocateResponse = 0x0103 typeAllocateErrorResponse = 0x0113 typeRefresh = 0x0004 typeRefreshResponse = 0x0104 typeRefreshErrorResponse = 0x0114 typeSend = 0x0006 typeSendResponse = 0x0106 typeSendErrorResponse = 0x0116 typeData = 0x0007 typeDataResponse = 0x0107 typeDataErrorResponse = 0x0117 typeCreatePermisiion = 0x0008 typeCreatePermisiionResponse = 0x0108 typeCreatePermisiionErrorResponse = 0x0118 typeChannelBinding = 0x0009 typeChannelBindingResponse = 0x0109 typeChannelBindingErrorResponse = 0x0119 typeConnect = 0x000a typeConnectResponse = 0x010a typeConnectErrorResponse = 0x011a typeConnectionBind = 0x000b typeConnectionBindResponse = 0x010b typeConnectionBindErrorResponse = 0x011b typeConnectionAttempt = 0x000c typeConnectionAttemptResponse = 0x010c typeConnectionAttemptErrorResponse = 0x011c ) go-stun-0.1.3/stun/discover.go000066400000000000000000000145641403514004000162460ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "errors" "net" ) // Follow RFC 3489 and RFC 5389. // Figure 2: Flow for type discovery process (from RFC 3489). // +--------+ // | Test | // | I | // +--------+ // | // | // V // /\ /\ // N / \ Y / \ Y +--------+ // UDP <-------/Resp\--------->/ IP \------------->| Test | // Blocked \ ? / \Same/ | II | // \ / \? / +--------+ // \/ \/ | // | N | // | V // V /\ // +--------+ Sym. N / \ // | Test | UDP <---/Resp\ // | II | Firewall \ ? / // +--------+ \ / // | \/ // V |Y // /\ /\ | // Symmetric N / \ +--------+ N / \ V // NAT <--- / IP \<-----| Test |<--- /Resp\ Open // \Same/ | I | \ ? / Internet // \? / +--------+ \ / // \/ \/ // |Y |Y // | | // | V // | Full // | Cone // V /\ // +--------+ / \ Y // | Test |------>/Resp\---->Restricted // | III | \ ? / // +--------+ \ / // \/ // |N // | Port // +------>Restricted func (c *Client) discover(conn net.PacketConn, addr *net.UDPAddr) (NATType, *Host, error) { // Perform test1 to check if it is under NAT. c.logger.Debugln("Do Test1") c.logger.Debugln("Send To:", addr) resp, err := c.test1(conn, addr) if err != nil { return NATError, nil, err } c.logger.Debugln("Received:", resp) if resp == nil { return NATBlocked, nil, nil } // identical used to check if it is open Internet or not. identical := resp.identical // changedAddr is used to perform second time test1 and test3. changedAddr := resp.changedAddr // mappedAddr is used as the return value, its IP is used for tests mappedAddr := resp.mappedAddr // Make sure IP and port are not changed. if resp.serverAddr.IP() != addr.IP.String() || resp.serverAddr.Port() != uint16(addr.Port) { return NATError, mappedAddr, errors.New("Server error: response IP/port") } // if changedAddr is not available, use otherAddr as changedAddr, // which is updated in RFC 5780 if changedAddr == nil { changedAddr = resp.otherAddr } // changedAddr shall not be nil if changedAddr == nil { return NATError, mappedAddr, errors.New("Server error: no changed address.") } // Perform test2 to see if the client can receive packet sent from // another IP and port. c.logger.Debugln("Do Test2") c.logger.Debugln("Send To:", addr) resp, err = c.test2(conn, addr) if err != nil { return NATError, mappedAddr, err } c.logger.Debugln("Received:", resp) // Make sure IP and port are changed. if resp != nil && (resp.serverAddr.IP() == addr.IP.String() || resp.serverAddr.Port() == uint16(addr.Port)) { return NATError, mappedAddr, errors.New("Server error: response IP/port") } if identical { if resp == nil { return SymmetricUDPFirewall, mappedAddr, nil } return NATNone, mappedAddr, nil } if resp != nil { return NATFull, mappedAddr, nil } // Perform test1 to another IP and port to see if the NAT use the same // external IP. c.logger.Debugln("Do Test1") c.logger.Debugln("Send To:", changedAddr) caddr, err := net.ResolveUDPAddr("udp", changedAddr.String()) if err != nil { c.logger.Debugf("ResolveUDPAddr error: %v", err) } resp, err = c.test1(conn, caddr) if err != nil { return NATError, mappedAddr, err } c.logger.Debugln("Received:", resp) if resp == nil { // It should be NAT_BLOCKED, but will be detected in the first // step. So this will never happen. return NATUnknown, mappedAddr, nil } // Make sure IP/port is not changed. if resp.serverAddr.IP() != caddr.IP.String() || resp.serverAddr.Port() != uint16(caddr.Port) { return NATError, mappedAddr, errors.New("Server error: response IP/port") } if mappedAddr.IP() == resp.mappedAddr.IP() && mappedAddr.Port() == resp.mappedAddr.Port() { // Perform test3 to see if the client can receive packet sent // from another port. c.logger.Debugln("Do Test3") c.logger.Debugln("Send To:", caddr) resp, err = c.test3(conn, caddr) if err != nil { return NATError, mappedAddr, err } c.logger.Debugln("Received:", resp) if resp == nil { return NATPortRestricted, mappedAddr, nil } // Make sure IP is not changed, and port is changed. if resp.serverAddr.IP() != caddr.IP.String() || resp.serverAddr.Port() == uint16(caddr.Port) { return NATError, mappedAddr, errors.New("Server error: response IP/port") } return NATRestricted, mappedAddr, nil } return NATSymmetric, mappedAddr, nil } go-stun-0.1.3/stun/doc.go000066400000000000000000000015131403514004000151630ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package stun is a STUN (RFC 3489 and RFC 5389) client implementation in // golang. // // It is extremely easy to use -- just one line of code. // // nat, host, err := stun.NewClient().Discover() // // More details please go to `main.go`. package stun go-stun-0.1.3/stun/host.go000066400000000000000000000032641403514004000154000ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "net" "strconv" ) // Host defines the network address including address family, IP address and port. type Host struct { family uint16 ip string port uint16 } func newHostFromStr(s string) *Host { udpAddr, err := net.ResolveUDPAddr("udp", s) if err != nil { return nil } host := new(Host) if udpAddr.IP.To4() != nil { host.family = attributeFamilyIPv4 } else { host.family = attributeFamilyIPV6 } host.ip = udpAddr.IP.String() host.port = uint16(udpAddr.Port) return host } // Family returns the family type of a host (IPv4 or IPv6). func (h *Host) Family() uint16 { return h.family } // IP returns the internet protocol address of the host. func (h *Host) IP() string { return h.ip } // Port returns the port number of the host. func (h *Host) Port() uint16 { return h.port } // TransportAddr returns the transport layer address of the host. func (h *Host) TransportAddr() string { return net.JoinHostPort(h.ip, strconv.Itoa(int(h.port))) } // String returns the string representation of the host address. func (h *Host) String() string { return h.TransportAddr() } go-stun-0.1.3/stun/log.go000066400000000000000000000036651403514004000152110ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "log" "os" ) // Logger is a simple logger specified for this STUN client. type Logger struct { log.Logger debug bool info bool } // NewLogger creates a default logger. func NewLogger() *Logger { logger := &Logger{*log.New(os.Stdout, "", log.LstdFlags), false, false} return logger } // SetDebug sets the logger running in debug mode or not. func (l *Logger) SetDebug(v bool) { l.debug = v } // SetInfo sets the logger running in info mode or not. func (l *Logger) SetInfo(v bool) { l.info = v } // Debug outputs the log in the format of log.Print. func (l *Logger) Debug(v ...interface{}) { if l.debug { l.Print(v...) } } // Debugf outputs the log in the format of log.Printf. func (l *Logger) Debugf(format string, v ...interface{}) { if l.debug { l.Printf(format, v...) } } // Debugln outputs the log in the format of log.Println. func (l *Logger) Debugln(v ...interface{}) { if l.debug { l.Println(v...) } } // Info outputs the log in the format of log.Print. func (l *Logger) Info(v ...interface{}) { if l.info { l.Print(v...) } } // Infof outputs the log in the format of log.Printf. func (l *Logger) Infof(format string, v ...interface{}) { if l.info { l.Printf(format, v...) } } // Infoln outputs the log in the format of log.Println. func (l *Logger) Infoln(v ...interface{}) { if l.info { l.Println(v...) } } go-stun-0.1.3/stun/net.go000066400000000000000000000057601403514004000152140ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "bytes" "encoding/hex" "errors" "net" "time" ) const ( numRetransmit = 9 defaultTimeout = 100 maxTimeout = 1600 maxPacketSize = 1024 ) func (c *Client) sendBindingReq(conn net.PacketConn, addr net.Addr, changeIP bool, changePort bool) (*response, error) { // Construct packet. pkt, err := newPacket() if err != nil { return nil, err } pkt.types = typeBindingRequest attribute := newSoftwareAttribute(c.softwareName) pkt.addAttribute(*attribute) if changeIP || changePort { attribute = newChangeReqAttribute(changeIP, changePort) pkt.addAttribute(*attribute) } // length of fingerprint attribute must be included into crc, // so we add it before calculating crc, then subtract it after calculating crc. pkt.length += 8 attribute = newFingerprintAttribute(pkt) pkt.length -= 8 pkt.addAttribute(*attribute) // Send packet. return c.send(pkt, conn, addr) } // RFC 3489: Clients SHOULD retransmit the request starting with an interval // of 100ms, doubling every retransmit until the interval reaches 1.6s. // Retransmissions continue with intervals of 1.6s until a response is // received, or a total of 9 requests have been sent. func (c *Client) send(pkt *packet, conn net.PacketConn, addr net.Addr) (*response, error) { c.logger.Info("\n" + hex.Dump(pkt.bytes())) timeout := defaultTimeout packetBytes := make([]byte, maxPacketSize) for i := 0; i < numRetransmit; i++ { // Send packet to the server. length, err := conn.WriteTo(pkt.bytes(), addr) if err != nil { return nil, err } if length != len(pkt.bytes()) { return nil, errors.New("Error in sending data.") } err = conn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond)) if err != nil { return nil, err } if timeout < maxTimeout { timeout *= 2 } for { // Read from the port. length, raddr, err := conn.ReadFrom(packetBytes) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { break } return nil, err } p, err := newPacketFromBytes(packetBytes[0:length]) if err != nil { return nil, err } // If transId mismatches, keep reading until get a // matched packet or timeout. if !bytes.Equal(pkt.transID, p.transID) { continue } c.logger.Info("\n" + hex.Dump(packetBytes[0:length])) resp := newResponse(p, conn) resp.serverAddr = newHostFromStr(raddr.String()) return resp, err } } return nil, nil } go-stun-0.1.3/stun/packet.go000066400000000000000000000070351403514004000156720ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "crypto/rand" "encoding/binary" "errors" "math" ) type packet struct { types uint16 length uint16 transID []byte // 4 bytes magic cookie + 12 bytes transaction id attributes []attribute } func newPacket() (*packet, error) { v := new(packet) v.transID = make([]byte, 16) binary.BigEndian.PutUint32(v.transID[:4], magicCookie) _, err := rand.Read(v.transID[4:]) if err != nil { return nil, err } v.attributes = make([]attribute, 0, 10) v.length = 0 return v, nil } func newPacketFromBytes(packetBytes []byte) (*packet, error) { if len(packetBytes) < 20 { return nil, errors.New("Received data length too short.") } if len(packetBytes) > math.MaxUint16+20 { return nil, errors.New("Received data length too long.") } pkt := new(packet) pkt.types = binary.BigEndian.Uint16(packetBytes[0:2]) pkt.length = binary.BigEndian.Uint16(packetBytes[2:4]) pkt.transID = packetBytes[4:20] pkt.attributes = make([]attribute, 0, 10) packetBytes = packetBytes[20:] for pos := uint16(0); pos+4 < uint16(len(packetBytes)); { types := binary.BigEndian.Uint16(packetBytes[pos : pos+2]) length := binary.BigEndian.Uint16(packetBytes[pos+2 : pos+4]) end := pos + 4 + length if end < pos+4 || end > uint16(len(packetBytes)) { return nil, errors.New("Received data format mismatch.") } value := packetBytes[pos+4 : end] attribute := newAttribute(types, value) pkt.addAttribute(*attribute) pos += align(length) + 4 } return pkt, nil } func (v *packet) addAttribute(a attribute) { v.attributes = append(v.attributes, a) v.length += align(a.length) + 4 } func (v *packet) bytes() []byte { packetBytes := make([]byte, 4) binary.BigEndian.PutUint16(packetBytes[0:2], v.types) binary.BigEndian.PutUint16(packetBytes[2:4], v.length) packetBytes = append(packetBytes, v.transID...) for _, a := range v.attributes { buf := make([]byte, 2) binary.BigEndian.PutUint16(buf, a.types) packetBytes = append(packetBytes, buf...) binary.BigEndian.PutUint16(buf, a.length) packetBytes = append(packetBytes, buf...) packetBytes = append(packetBytes, a.value...) } return packetBytes } func (v *packet) getSourceAddr() *Host { return v.getRawAddr(attributeSourceAddress) } func (v *packet) getMappedAddr() *Host { return v.getRawAddr(attributeMappedAddress) } func (v *packet) getChangedAddr() *Host { return v.getRawAddr(attributeChangedAddress) } func (v *packet) getOtherAddr() *Host { return v.getRawAddr(attributeOtherAddress) } func (v *packet) getRawAddr(attribute uint16) *Host { for _, a := range v.attributes { if a.types == attribute { return a.rawAddr() } } return nil } func (v *packet) getXorMappedAddr() *Host { addr := v.getXorAddr(attributeXorMappedAddress) if addr == nil { addr = v.getXorAddr(attributeXorMappedAddressExp) } return addr } func (v *packet) getXorAddr(attribute uint16) *Host { for _, a := range v.attributes { if a.types == attribute { return a.xorAddr(v.transID) } } return nil } go-stun-0.1.3/stun/packet_test.go000066400000000000000000000027251403514004000167320ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "testing" ) func TestNewPacketFromBytes(t *testing.T) { b := make([]byte, 19) _, err := newPacketFromBytes(b) if err == nil { t.Errorf("newPacketFromBytes error") } b = make([]byte, 20) _, err = newPacketFromBytes(b) if err != nil { t.Errorf("newPacketFromBytes error") } } func TestNewPacket(t *testing.T) { _, err := newPacket() if err != nil { t.Errorf("newPacket error") } } func TestPacketAll(t *testing.T) { p, err := newPacket() if err != nil { t.Errorf("newPacket error") } p.addAttribute(*newChangeReqAttribute(true, true)) p.addAttribute(*newSoftwareAttribute("aaa")) p.addAttribute(*newFingerprintAttribute(p)) pkt, err := newPacketFromBytes(p.bytes()) if err != nil { t.Errorf("newPacketFromBytes error") } if pkt.types != 0 { t.Errorf("newPacketFromBytes error") } if pkt.length < 20 { t.Errorf("newPacketFromBytes error") } } go-stun-0.1.3/stun/response.go000066400000000000000000000043101403514004000162520ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "fmt" "net" ) type response struct { packet *packet // the original packet from the server serverAddr *Host // the address received packet changedAddr *Host // parsed from packet mappedAddr *Host // parsed from packet, external addr of client NAT otherAddr *Host // parsed from packet, to replace changedAddr in RFC 5780 identical bool // if mappedAddr is in local addr list } func newResponse(pkt *packet, conn net.PacketConn) *response { resp := &response{pkt, nil, nil, nil, nil, false} if pkt == nil { return resp } // RFC 3489 doesn't require the server return XOR mapped address. mappedAddr := pkt.getXorMappedAddr() if mappedAddr == nil { mappedAddr = pkt.getMappedAddr() } resp.mappedAddr = mappedAddr // compute identical localAddrStr := conn.LocalAddr().String() if mappedAddr != nil { mappedAddrStr := mappedAddr.String() resp.identical = isLocalAddress(localAddrStr, mappedAddrStr) } // compute changedAddr changedAddr := pkt.getChangedAddr() if changedAddr != nil { changedAddrHost := newHostFromStr(changedAddr.String()) resp.changedAddr = changedAddrHost } // compute otherAddr otherAddr := pkt.getOtherAddr() if otherAddr != nil { otherAddrHost := newHostFromStr(otherAddr.String()) resp.otherAddr = otherAddrHost } return resp } // String is only used for verbose mode output. func (r *response) String() string { if r == nil { return "Nil" } return fmt.Sprintf("{packet nil: %v, local: %v, remote: %v, changed: %v, other: %v, identical: %v}", r.packet == nil, r.mappedAddr, r.serverAddr, r.changedAddr, r.otherAddr, r.identical) } go-stun-0.1.3/stun/tests.go000066400000000000000000000017731403514004000155700ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "net" ) func (c *Client) test1(conn net.PacketConn, addr net.Addr) (*response, error) { return c.sendBindingReq(conn, addr, false, false) } func (c *Client) test2(conn net.PacketConn, addr net.Addr) (*response, error) { return c.sendBindingReq(conn, addr, true, true) } func (c *Client) test3(conn net.PacketConn, addr net.Addr) (*response, error) { return c.sendBindingReq(conn, addr, false, true) } go-stun-0.1.3/stun/utils.go000066400000000000000000000034161403514004000155620ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "net" ) // Padding the length of the byte slice to multiple of 4. func padding(bytes []byte) []byte { length := uint16(len(bytes)) return append(bytes, make([]byte, align(length)-length)...) } // Align the uint16 number to the smallest multiple of 4, which is larger than // or equal to the uint16 number. func align(n uint16) uint16 { return (n + 3) & 0xfffc } // isLocalAddress check if localRemote is a local address. func isLocalAddress(local, localRemote string) bool { // Resolve the IP returned by the STUN server first. localRemoteAddr, err := net.ResolveUDPAddr("udp", localRemote) if err != nil { return false } // Try comparing with the local address on the socket first, but only if // it's actually specified. addr, err := net.ResolveUDPAddr("udp", local) if err == nil && addr.IP != nil && !addr.IP.IsUnspecified() { return addr.IP.Equal(localRemoteAddr.IP) } // Fallback to checking IPs of all interfaces addrs, err := net.InterfaceAddrs() if err != nil { return false } for _, addr := range addrs { ip, _, err := net.ParseCIDR(addr.String()) if err != nil { continue } if ip.Equal(localRemoteAddr.IP) { return true } } return false } go-stun-0.1.3/stun/utils_test.go000066400000000000000000000031361403514004000166200ustar00rootroot00000000000000// Copyright 2016 Cong Ding // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package stun import ( "testing" ) func TestPadding(t *testing.T) { b := []byte{1, 2} expected := []byte{1, 2, 0, 0} result := padding(b) if len(result) != len(expected) { t.Errorf("Padding error: result size wrong.\n") } for i := range expected { if expected[i] != result[i] { t.Errorf("Padding error: data wrong in bit %d.\n", i) } } } func TestAlign(t *testing.T) { d := make(map[uint16]uint16) d[1] = 4 d[4] = 4 d[5] = 8 d[6] = 8 d[7] = 8 d[8] = 8 d[65528] = 65528 d[65529] = 65532 d[65531] = 65532 d[65532] = 65532 for k, v := range d { if align(k) != v { t.Errorf("Align error: expected %d, get %d", align(k), v) } } } func TestIsLocalAddress(t *testing.T) { if !isLocalAddress(":1234", "127.0.0.1:8888") { t.Errorf("isLocal error") } if !isLocalAddress("192.168.0.1:1234", "192.168.0.1:8888") { t.Errorf("isLocal error") } if !isLocalAddress("8.8.8.8:1234", "8.8.8.8:8888") { t.Errorf("isLocal error") } if isLocalAddress(":1234", "8.8.8.8:8888") { t.Errorf("isLocal error") } }