刘河 %!s(int64=6) %!d(string=hai) anos
pai
achega
2330935b90

+ 25 - 0
vender/github.com/ccding/go-stun/.gitignore

@@ -0,0 +1,25 @@
+# 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

+ 191 - 0
vender/github.com/ccding/go-stun/LICENSE

@@ -0,0 +1,191 @@
+Apache 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.

+ 27 - 0
vender/github.com/ccding/go-stun/linter_config.json

@@ -0,0 +1,27 @@
+{
+	"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": []
+}

+ 56 - 0
vender/github.com/ccding/go-stun/main.go

@@ -0,0 +1,56 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// author: Cong Ding <dinggnu@gmail.com>
+
+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())
+	}
+}

+ 106 - 0
vender/github.com/ccding/go-stun/stun/attribute.go

@@ -0,0 +1,106 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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
+}

+ 126 - 0
vender/github.com/ccding/go-stun/stun/client.go

@@ -0,0 +1,126 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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
+}

+ 178 - 0
vender/github.com/ccding/go-stun/stun/const.go

@@ -0,0 +1,178 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+package stun
+
+// Default server address and client name.
+const (
+	DefaultServerAddr   = "stun.voiparound.com: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
+	NATSymmetricUDPFirewall
+
+	// Deprecated spellings of these constants
+	NATSymetric            = NATSymmetric
+	NATSymetricUDPFirewall = NATSymmetricUDPFirewall
+)
+
+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",
+		NATSymmetricUDPFirewall: "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
+)

+ 168 - 0
vender/github.com/ccding/go-stun/stun/discover.go

@@ -0,0 +1,168 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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 NATSymmetricUDPFirewall, 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
+}

+ 25 - 0
vender/github.com/ccding/go-stun/stun/doc.go

@@ -0,0 +1,25 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+// 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

+ 70 - 0
vender/github.com/ccding/go-stun/stun/host.go

@@ -0,0 +1,70 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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()
+}

+ 87 - 0
vender/github.com/ccding/go-stun/stun/log.go

@@ -0,0 +1,87 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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...)
+	}
+}

+ 106 - 0
vender/github.com/ccding/go-stun/stun/net.go

@@ -0,0 +1,106 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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
+}

+ 129 - 0
vender/github.com/ccding/go-stun/stun/packet.go

@@ -0,0 +1,129 @@
+// Copyright 2013, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+package stun
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	"errors"
+)
+
+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) < 24 {
+		return nil, errors.New("Received data length too short.")
+	}
+	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)
+	for pos := uint16(20); pos < uint16(len(packetBytes)); {
+		types := binary.BigEndian.Uint16(packetBytes[pos : pos+2])
+		length := binary.BigEndian.Uint16(packetBytes[pos+2 : pos+4])
+		if pos+4+length > uint16(len(packetBytes)) {
+			return nil, errors.New("Received data format mismatch.")
+		}
+		value := packetBytes[pos+4 : pos+4+length]
+		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
+}

+ 61 - 0
vender/github.com/ccding/go-stun/stun/packet_test.go

@@ -0,0 +1,61 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+package stun
+
+import (
+	"testing"
+)
+
+func TestNewPacketFromBytes(t *testing.T) {
+	b := make([]byte, 23)
+	_, err := newPacketFromBytes(b)
+	if err == nil {
+		t.Errorf("newPacketFromBytes error")
+	}
+	b = make([]byte, 24)
+	_, 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 < 24 {
+		t.Errorf("newPacketFromBytes error")
+	}
+}

+ 78 - 0
vender/github.com/ccding/go-stun/stun/response.go

@@ -0,0 +1,78 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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)
+}

+ 33 - 0
vender/github.com/ccding/go-stun/stun/tests.go

@@ -0,0 +1,33 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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)
+}

+ 63 - 0
vender/github.com/ccding/go-stun/stun/utils.go

@@ -0,0 +1,63 @@
+// Copyright 2016, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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
+}

+ 69 - 0
vender/github.com/ccding/go-stun/stun/utils_test.go

@@ -0,0 +1,69 @@
+// Copyright 2015, Cong Ding. All rights reserved.
+//
+// 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.
+//
+// Author: Cong Ding <dinggnu@gmail.com>
+
+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")
+	}
+}