summaryrefslogtreecommitdiff
path: root/vendor/git.torproject.org
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-11-29 01:46:27 +0100
committerkali kaneko (leap communications) <kali@leap.se>2021-11-29 18:14:16 +0100
commit18f52af5be3a9a0c73811706108f790d65ee9c67 (patch)
treee13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/git.torproject.org
parentebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff)
[pkg] update vendor
Diffstat (limited to 'vendor/git.torproject.org')
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/.gitignore2
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING121
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/ChangeLog60
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/README27
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go219
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go1033
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go507
-rw-r--r--vendor/git.torproject.org/pluggable-transports/goptlib.git/test_authcookie2
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/LICENSE32
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/encapsulation/encapsulation.go194
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/nat/nat.go246
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/safelog/log.go71
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientid.go28
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientmap.go145
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/consts.go17
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/queuepacketconn.go137
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/redialpacketconn.go204
-rw-r--r--vendor/git.torproject.org/pluggable-transports/snowflake.git/common/util/util.go99
18 files changed, 3144 insertions, 0 deletions
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/.gitignore b/vendor/git.torproject.org/pluggable-transports/goptlib.git/.gitignore
new file mode 100644
index 0000000..d4d5132
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/.gitignore
@@ -0,0 +1,2 @@
+/examples/dummy-client/dummy-client
+/examples/dummy-server/dummy-server
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING b/vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/ChangeLog b/vendor/git.torproject.org/pluggable-transports/goptlib.git/ChangeLog
new file mode 100644
index 0000000..2aae857
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/ChangeLog
@@ -0,0 +1,60 @@
+== v1.1.0
+
+Added the Log function.
+https://bugs.torproject.org/28940
+
+== v1.0.0
+
+Changed the tag naming scheme to work better with Go modules.
+https://github.com/golang/go/wiki/Modules#semantic-import-versioning
+
+== 0.7
+
+Fixed the ProxyError function; previously it would always panic.
+
+Repeated transport names in TOR_PT_SERVER_BINDADDR now result in an
+ENV-ERROR.
+https://bugs.torproject.org/21261
+
+== 0.6
+
+Remove all support for the "*" transport specification. The argument to
+the ClientSetup and ServerSetup functions is now unused.
+https://bugs.torproject.org/15612
+
+Replaced SOCKS4a with SOCKS5.
+https://bugs.torproject.org/12535
+
+== 0.5
+
+The AcceptSocks function no longer reports non-permanent errors, such as
+those caused by a faulty SOCKS handshake.
+
+Added support for an upstream proxy (TOR_PT_PROXY). The two new
+functions are ProxyError and ProxyDone. The ClientInfo struct has a new
+ProxyURL member.
+https://bugs.torproject.org/12125
+
+== 0.4
+
+Read the ExtORPort cookie file on every call to DialOr, instead of
+reading it once and caching the result. This is to work around a tor bug
+where tor doesn't ensure a new cookie file is written before starting
+pluggable transports.
+https://bugs.torproject.org/15240
+
+== 0.3
+
+Made output functions panic intead of backslash-escaping. Escaping of
+invalid bytes is not specified by pt-spec, and backslashes conflicted
+with the specified escaping of SMETHOD ARGS.
+https://bugs.torproject.org/13370
+
+== 0.2
+
+Added the MakeStateDir function.
+
+== 0.1
+== 0.0
+
+Initial release.
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/README b/vendor/git.torproject.org/pluggable-transports/goptlib.git/README
new file mode 100644
index 0000000..dc605d1
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/README
@@ -0,0 +1,27 @@
+goptlib is a library for writing Tor pluggable transports in Go.
+
+https://spec.torproject.org/pt-spec
+https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
+https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
+https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
+
+To download a copy of the library into $GOPATH:
+ go get git.torproject.org/pluggable-transports/goptlib.git
+
+See the included example programs for examples of how to use the
+library. To build them, enter their directory and run "go build".
+ examples/dummy-client/dummy-client.go
+ examples/dummy-server/dummy-server.go
+The recommended way to start writing a new transport plugin is to copy
+dummy-client or dummy-server and make changes to it.
+
+There is browseable documentation here:
+https://godoc.org/git.torproject.org/pluggable-transports/goptlib.git
+
+Report bugs to the tor-dev@lists.torproject.org mailing list or to the
+bug tracker at https://trac.torproject.org/projects/tor.
+
+To the extent possible under law, the authors have dedicated all
+copyright and related and neighboring rights to this software to the
+public domain worldwide. This software is distributed without any
+warranty. See COPYING.
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go b/vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go
new file mode 100644
index 0000000..c50bc93
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/args.go
@@ -0,0 +1,219 @@
+package pt
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// Key–value mappings for the representation of client and server options.
+
+// Args maps a string key to a list of values. It is similar to url.Values.
+type Args map[string][]string
+
+// Get the first value associated with the given key. If there are any values
+// associated with the key, the value return has the value and ok is set to
+// true. If there are no values for the given key, value is "" and ok is false.
+// If you need access to multiple values, use the map directly.
+func (args Args) Get(key string) (value string, ok bool) {
+ if args == nil {
+ return "", false
+ }
+ vals, ok := args[key]
+ if !ok || len(vals) == 0 {
+ return "", false
+ }
+ return vals[0], true
+}
+
+// Append value to the list of values for key.
+func (args Args) Add(key, value string) {
+ args[key] = append(args[key], value)
+}
+
+// Return the index of the next unescaped byte in s that is in the term set, or
+// else the length of the string if no terminators appear. Additionally return
+// the unescaped string up to the returned index.
+func indexUnescaped(s string, term []byte) (int, string, error) {
+ var i int
+ unesc := make([]byte, 0)
+ for i = 0; i < len(s); i++ {
+ b := s[i]
+ // A terminator byte?
+ if bytes.IndexByte(term, b) != -1 {
+ break
+ }
+ if b == '\\' {
+ i++
+ if i >= len(s) {
+ return 0, "", fmt.Errorf("nothing following final escape in %q", s)
+ }
+ b = s[i]
+ }
+ unesc = append(unesc, b)
+ }
+ return i, string(unesc), nil
+}
+
+// Parse a name–value mapping as from an encoded SOCKS username/password.
+//
+// "First the '<Key>=<Value>' formatted arguments MUST be escaped, such that all
+// backslash, equal sign, and semicolon characters are escaped with a
+// backslash."
+func parseClientParameters(s string) (args Args, err error) {
+ args = make(Args)
+ if len(s) == 0 {
+ return
+ }
+ i := 0
+ for {
+ var key, value string
+ var offset, begin int
+
+ begin = i
+ // Read the key.
+ offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no equals sign?
+ if i >= len(s) || s[i] != '=' {
+ err = fmt.Errorf("no equals sign in %q", s[begin:i])
+ return
+ }
+ // Skip the equals sign.
+ i++
+ // Read the value.
+ offset, value, err = indexUnescaped(s[i:], []byte{';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ if len(key) == 0 {
+ err = fmt.Errorf("empty key in %q", s[begin:i])
+ return
+ }
+ args.Add(key, value)
+ if i >= len(s) {
+ break
+ }
+ // Skip the semicolon.
+ i++
+ }
+ return args, nil
+}
+
+// Parse a transport–name–value mapping as from TOR_PT_SERVER_TRANSPORT_OPTIONS.
+//
+// "...a semicolon-separated list of <key>:<value> pairs, where <key> is a PT
+// name and <value> is a k=v string value with options that are to be passed to
+// the transport. Colons, semicolons, equal signs and backslashes must be
+// escaped with a backslash."
+// Example: scramblesuit:key=banana;automata:rule=110;automata:depth=3
+func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
+ opts = make(map[string]Args)
+ if len(s) == 0 {
+ return
+ }
+ i := 0
+ for {
+ var methodName, key, value string
+ var offset, begin int
+
+ begin = i
+ // Read the method name.
+ offset, methodName, err = indexUnescaped(s[i:], []byte{':', '=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no colon?
+ if i >= len(s) || s[i] != ':' {
+ err = fmt.Errorf("no colon in %q", s[begin:i])
+ return
+ }
+ // Skip the colon.
+ i++
+ // Read the key.
+ offset, key, err = indexUnescaped(s[i:], []byte{'=', ';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ // End of string or no equals sign?
+ if i >= len(s) || s[i] != '=' {
+ err = fmt.Errorf("no equals sign in %q", s[begin:i])
+ return
+ }
+ // Skip the equals sign.
+ i++
+ // Read the value.
+ offset, value, err = indexUnescaped(s[i:], []byte{';'})
+ if err != nil {
+ return
+ }
+ i += offset
+ if len(methodName) == 0 {
+ err = fmt.Errorf("empty method name in %q", s[begin:i])
+ return
+ }
+ if len(key) == 0 {
+ err = fmt.Errorf("empty key in %q", s[begin:i])
+ return
+ }
+ if opts[methodName] == nil {
+ opts[methodName] = make(Args)
+ }
+ opts[methodName].Add(key, value)
+ if i >= len(s) {
+ break
+ }
+ // Skip the semicolon.
+ i++
+ }
+ return opts, nil
+}
+
+// Escape backslashes and all the bytes that are in set.
+func backslashEscape(s string, set []byte) string {
+ var buf bytes.Buffer
+ for _, b := range []byte(s) {
+ if b == '\\' || bytes.IndexByte(set, b) != -1 {
+ buf.WriteByte('\\')
+ }
+ buf.WriteByte(b)
+ }
+ return buf.String()
+}
+
+// Encode a name–value mapping so that it is suitable to go in the ARGS option
+// of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
+// added.
+//
+// "Equal signs and commas [and backslashes] MUST be escaped with a backslash."
+func encodeSmethodArgs(args Args) string {
+ if args == nil {
+ return ""
+ }
+
+ keys := make([]string, 0, len(args))
+ for key := range args {
+ keys = append(keys, key)
+ }
+ sort.Strings(keys)
+
+ escape := func(s string) string {
+ return backslashEscape(s, []byte{'=', ','})
+ }
+
+ var pairs []string
+ for _, key := range keys {
+ for _, value := range args[key] {
+ pairs = append(pairs, escape(key)+"="+escape(value))
+ }
+ }
+
+ return strings.Join(pairs, ",")
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go b/vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go
new file mode 100644
index 0000000..23893f4
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/pt.go
@@ -0,0 +1,1033 @@
+// Package pt implements the Tor pluggable transports specification.
+//
+// Sample client usage:
+// var ptInfo pt.ClientInfo
+// ...
+// func handler(conn *pt.SocksConn) error {
+// defer conn.Close()
+// remote, err := net.Dial("tcp", conn.Req.Target)
+// if err != nil {
+// conn.Reject()
+// return err
+// }
+// defer remote.Close()
+// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
+// if err != nil {
+// return err
+// }
+// // do something with conn and remote.
+// return nil
+// }
+// func acceptLoop(ln *pt.SocksListener) error {
+// defer ln.Close()
+// for {
+// conn, err := ln.AcceptSocks()
+// if err != nil {
+// if e, ok := err.(net.Error); ok && e.Temporary() {
+// pt.Log(pt.LogSeverityError, "accept error: " + err.Error())
+// continue
+// }
+// return err
+// }
+// go handler(conn)
+// }
+// return nil
+// }
+// ...
+// func main() {
+// var err error
+// ptInfo, err = pt.ClientSetup(nil)
+// if err != nil {
+// os.Exit(1)
+// }
+// if ptInfo.ProxyURL != nil {
+// // you need to interpret the proxy URL yourself
+// // call pt.ProxyDone instead if it's a type you understand
+// pt.ProxyError(fmt.Sprintf("proxy %s is not supported", ptInfo.ProxyURL))
+// os.Exit(1)
+// }
+// for _, methodName := range ptInfo.MethodNames {
+// switch methodName {
+// case "foo":
+// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
+// if err != nil {
+// pt.CmethodError(methodName, err.Error())
+// break
+// }
+// go acceptLoop(ln)
+// pt.Cmethod(methodName, ln.Version(), ln.Addr())
+// default:
+// pt.CmethodError(methodName, "no such method")
+// }
+// }
+// pt.CmethodsDone()
+// }
+//
+// Sample server usage:
+// var ptInfo pt.ServerInfo
+// ...
+// func handler(conn net.Conn) error {
+// defer conn.Close()
+// or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "foo")
+// if err != nil {
+// return
+// }
+// defer or.Close()
+// // do something with or and conn
+// return nil
+// }
+// func acceptLoop(ln net.Listener) error {
+// defer ln.Close()
+// for {
+// conn, err := ln.Accept()
+// if err != nil {
+// if e, ok := err.(net.Error); ok && e.Temporary() {
+// continue
+// }
+// pt.Log(pt.LogSeverityError, "accept error: " + err.Error())
+// return err
+// }
+// go handler(conn)
+// }
+// return nil
+// }
+// ...
+// func main() {
+// var err error
+// ptInfo, err = pt.ServerSetup(nil)
+// if err != nil {
+// os.Exit(1)
+// }
+// for _, bindaddr := range ptInfo.Bindaddrs {
+// switch bindaddr.MethodName {
+// case "foo":
+// ln, err := net.ListenTCP("tcp", bindaddr.Addr)
+// if err != nil {
+// pt.SmethodError(bindaddr.MethodName, err.Error())
+// break
+// }
+// go acceptLoop(ln)
+// pt.Smethod(bindaddr.MethodName, ln.Addr())
+// default:
+// pt.SmethodError(bindaddr.MethodName, "no such method")
+// }
+// }
+// pt.SmethodsDone()
+// }
+//
+// Some additional care is needed to handle signals and shutdown properly. See
+// the example programs dummy-client and dummy-server.
+//
+// Tor pluggable transports specification:
+// https://spec.torproject.org/pt-spec
+//
+// Extended ORPort:
+// https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
+//
+// Extended ORPort Authentication:
+// https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
+//
+// Pluggable Transport through SOCKS proxy:
+// https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
+//
+// The package implements a SOCKS5 server sufficient for a Tor client transport
+// plugin.
+//
+// https://www.ietf.org/rfc/rfc1928.txt
+// https://www.ietf.org/rfc/rfc1929.txt
+package pt
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/rand"
+ "crypto/sha256"
+ "crypto/subtle"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// This type wraps a Write method and calls Sync after each Write.
+type syncWriter struct {
+ *os.File
+}
+
+// Call File.Write and then Sync. An error is returned if either operation
+// returns an error.
+func (w syncWriter) Write(p []byte) (n int, err error) {
+ n, err = w.File.Write(p)
+ if err != nil {
+ return
+ }
+ err = w.Sync()
+ return
+}
+
+// Writer to which pluggable transports negotiation messages are written. It
+// defaults to a Writer that writes to os.Stdout and calls Sync after each
+// write.
+//
+// You may, for example, log pluggable transports messages by defining a Writer
+// that logs what is written to it:
+// type logWriteWrapper struct {
+// io.Writer
+// }
+//
+// func (w logWriteWrapper) Write(p []byte) (int, error) {
+// log.Print(string(p))
+// return w.Writer.Write(p)
+// }
+// and then redefining Stdout:
+// pt.Stdout = logWriteWrapper{pt.Stdout}
+var Stdout io.Writer = syncWriter{os.Stdout}
+
+// Represents an error that can happen during negotiation, for example
+// ENV-ERROR. When an error occurs, we print it to stdout and also pass it up
+// the return chain.
+type ptErr struct {
+ Keyword string
+ Args []string
+}
+
+// Implements the error interface.
+func (err *ptErr) Error() string {
+ return formatline(err.Keyword, err.Args...)
+}
+
+func getenv(key string) string {
+ return os.Getenv(key)
+}
+
+// Returns an ENV-ERROR if the environment variable isn't set.
+func getenvRequired(key string) (string, error) {
+ value := os.Getenv(key)
+ if value == "" {
+ return "", envError(fmt.Sprintf("no %s environment variable", key))
+ }
+ return value, nil
+}
+
+// Returns true iff keyword contains only bytes allowed in a PT→Tor output line
+// keyword.
+// <KeywordChar> ::= <any US-ASCII alphanumeric, dash, and underscore>
+func keywordIsSafe(keyword string) bool {
+ for _, b := range []byte(keyword) {
+ switch {
+ case '0' <= b && b <= '9':
+ continue
+ case 'A' <= b && b <= 'Z':
+ continue
+ case 'a' <= b && b <= 'z':
+ continue
+ case b == '-' || b == '_':
+ continue
+ default:
+ return false
+ }
+ }
+ return true
+}
+
+// Returns true iff arg contains only bytes allowed in a PT→Tor output line arg.
+// <ArgChar> ::= <any US-ASCII character but NUL or NL>
+func argIsSafe(arg string) bool {
+ for _, b := range []byte(arg) {
+ if b >= '\x80' || b == '\x00' || b == '\n' {
+ return false
+ }
+ }
+ return true
+}
+
+func formatline(keyword string, v ...string) string {
+ var buf bytes.Buffer
+ if !keywordIsSafe(keyword) {
+ panic(fmt.Sprintf("keyword %q contains forbidden bytes", keyword))
+ }
+ buf.WriteString(keyword)
+ for _, x := range v {
+ if !argIsSafe(x) {
+ panic(fmt.Sprintf("arg %q contains forbidden bytes", x))
+ }
+ buf.WriteString(" " + x)
+ }
+ return buf.String()
+}
+
+// Print a pluggable transports protocol line to Stdout. The line consists of a
+// keyword followed by any number of space-separated arg strings. Panics if
+// there are forbidden bytes in the keyword or the args (pt-spec.txt 2.2.1).
+func line(keyword string, v ...string) {
+ fmt.Fprintln(Stdout, formatline(keyword, v...))
+}
+
+// Emit and return the given error as a ptErr.
+func doError(keyword string, v ...string) *ptErr {
+ line(keyword, v...)
+ return &ptErr{keyword, v}
+}
+
+// Emit an ENV-ERROR line with explanation text. Returns a representation of the
+// error.
+func envError(msg string) error {
+ return doError("ENV-ERROR", msg)
+}
+
+// Emit a VERSION-ERROR line with explanation text. Returns a representation of
+// the error.
+func versionError(msg string) error {
+ return doError("VERSION-ERROR", msg)
+}
+
+// Emit a CMETHOD-ERROR line with explanation text. Returns a representation of
+// the error.
+func CmethodError(methodName, msg string) error {
+ return doError("CMETHOD-ERROR", methodName, msg)
+}
+
+// Emit an SMETHOD-ERROR line with explanation text. Returns a representation of
+// the error.
+func SmethodError(methodName, msg string) error {
+ return doError("SMETHOD-ERROR", methodName, msg)
+}
+
+// Emit a PROXY-ERROR line with explanation text. Returns a representation of
+// the error.
+func ProxyError(msg string) error {
+ return doError("PROXY-ERROR", msg)
+}
+
+// Emit a CMETHOD line. socks must be "socks4" or "socks5". Call this once for
+// each listening client SOCKS port.
+func Cmethod(name string, socks string, addr net.Addr) {
+ line("CMETHOD", name, socks, addr.String())
+}
+
+// Emit a CMETHODS DONE line. Call this after opening all client listeners.
+func CmethodsDone() {
+ line("CMETHODS", "DONE")
+}
+
+// Emit an SMETHOD line. Call this once for each listening server port.
+func Smethod(name string, addr net.Addr) {
+ line("SMETHOD", name, addr.String())
+}
+
+// Emit an SMETHOD line with an ARGS option. args is a name–value mapping that
+// will be added to the server's extrainfo document.
+//
+// This is an example of how to check for a required option:
+// secret, ok := bindaddr.Options.Get("shared-secret")
+// if ok {
+// args := pt.Args{}
+// args.Add("shared-secret", secret)
+// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
+// } else {
+// pt.SmethodError(bindaddr.MethodName, "need a shared-secret option")
+// }
+// Or, if you just want to echo back the options provided by Tor from the
+// TransportServerOptions configuration,
+// pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), bindaddr.Options)
+func SmethodArgs(name string, addr net.Addr, args Args) {
+ line("SMETHOD", name, addr.String(), "ARGS:"+encodeSmethodArgs(args))
+}
+
+// Emit an SMETHODS DONE line. Call this after opening all server listeners.
+func SmethodsDone() {
+ line("SMETHODS", "DONE")
+}
+
+// Emit a PROXY DONE line. Call this after parsing ClientInfo.ProxyURL.
+func ProxyDone() {
+ fmt.Fprintf(Stdout, "PROXY DONE\n")
+}
+
+// Unexported type to represent log severities, preventing external callers from
+// inventing new severity strings that may violate quoting rules.
+//
+// pt-spec.txt section 3.3.4 specifies quoting for MESSAGE, but not for
+// SEVERITY, and the example shows an unquoted "SEVERITY=debug". While we know
+// tor's parser permits quoting of SEVERITY, it's not actually specified.
+// Therefore we we need to guard against callers passing a string that violates
+// the global protocol constraint of "any US-ASCII character but NUL or NL." So
+// here, we instantiate exactly the five supported severities, using a type that
+// cannot be constructed outside the package.
+type logSeverity struct {
+ string
+}
+
+// Severity levels for the Log function.
+var (
+ LogSeverityError = logSeverity{"error"}
+ LogSeverityWarning = logSeverity{"warning"}
+ LogSeverityNotice = logSeverity{"notice"}
+ LogSeverityInfo = logSeverity{"info"}
+ LogSeverityDebug = logSeverity{"debug"}
+)
+
+// Encode a string according to the CString rules of section 2.1.1 in
+// control-spec.txt.
+// CString = DQUOTE *qcontent DQUOTE
+// "...in a CString, the escapes '\n', '\t', '\r', and the octal escapes '\0'
+// ... '\377' represent newline, tab, carriage return, and the 256 possible
+// octet values respectively."
+// RFC 2822 section 3.2.5 in turn defines what byte values we need to escape:
+// everything but
+// NO-WS-CTL / ; Non white space controls
+// %d33 / ; The rest of the US-ASCII
+// %d35-91 / ; characters not including "\"
+// %d93-126 ; or the quote character
+// Technically control-spec.txt requires us to escape the space character (32),
+// but it is an error in the spec: https://bugs.torproject.org/29432.
+//
+// We additionally need to ensure that whatever we return passes argIsSafe,
+// because strings encoded by this function are printed verbatim by Log.
+func encodeCString(s string) string {
+ result := bytes.NewBuffer([]byte{})
+ result.WriteByte('"')
+ for _, c := range []byte(s) {
+ if c == 32 || c == 33 || (35 <= c && c <= 91) || (93 <= c && c <= 126) {
+ result.WriteByte(c)
+ } else {
+ fmt.Fprintf(result, "\\%03o", c)
+ }
+ }
+ result.WriteByte('"')
+ return result.String()
+}
+
+// Emit a LOG message with the given severity (one of LogSeverityError,
+// LogSeverityWarning, LogSeverityNotice, LogSeverityInfo, or LogSeverityDebug).
+func Log(severity logSeverity, message string) {
+ // "<Message> contains the log message which can be a String or CString..."
+ // encodeCString always makes the string safe to emit; i.e., it
+ // satisfies argIsSafe.
+ line("LOG", "SEVERITY="+severity.string, "MESSAGE="+encodeCString(message))
+}
+
+// Get a pluggable transports version offered by Tor and understood by us, if
+// any. The only version we understand is "1". This function reads the
+// environment variable TOR_PT_MANAGED_TRANSPORT_VER.
+func getManagedTransportVer() (string, error) {
+ const transportVersion = "1"
+ managedTransportVer, err := getenvRequired("TOR_PT_MANAGED_TRANSPORT_VER")
+ if err != nil {
+ return "", err
+ }
+ for _, offered := range strings.Split(managedTransportVer, ",") {
+ if offered == transportVersion {
+ return offered, nil
+ }
+ }
+ return "", versionError("no-version")
+}
+
+// Return the directory name in the TOR_PT_STATE_LOCATION environment variable,
+// creating it if it doesn't exist. Returns non-nil error if
+// TOR_PT_STATE_LOCATION is not set or if there is an error creating the
+// directory.
+func MakeStateDir() (string, error) {
+ dir, err := getenvRequired("TOR_PT_STATE_LOCATION")
+ if err != nil {
+ return "", err
+ }
+ err = os.MkdirAll(dir, 0700)
+ return dir, err
+}
+
+// Get the list of method names requested by Tor. This function reads the
+// environment variable TOR_PT_CLIENT_TRANSPORTS.
+func getClientTransports() ([]string, error) {
+ clientTransports, err := getenvRequired("TOR_PT_CLIENT_TRANSPORTS")
+ if err != nil {
+ return nil, err
+ }
+ return strings.Split(clientTransports, ","), nil
+}
+
+// Get the upstream proxy URL. Returns nil if no proxy is requested. The
+// function ensures that the Scheme and Host fields are set; i.e., that the URL
+// is absolute. It additionally checks that the Host field contains both a host
+// and a port part. This function reads the environment variable TOR_PT_PROXY.
+//
+// This function doesn't check that the scheme is one of Tor's supported proxy
+// schemes; that is, one of "http", "socks5", or "socks4a". The caller must be
+// able to handle any returned scheme (which may be by calling ProxyError if
+// it doesn't know how to handle the scheme).
+func getProxyURL() (*url.URL, error) {
+ rawurl := os.Getenv("TOR_PT_PROXY")
+ if rawurl == "" {
+ return nil, nil
+ }
+ u, err := url.Parse(rawurl)
+ if err != nil {
+ return nil, err
+ }
+ if u.Scheme == "" {
+ return nil, fmt.Errorf("missing scheme")
+ }
+ if u.Host == "" {
+ return nil, fmt.Errorf("missing authority")
+ }
+ host, port, err := net.SplitHostPort(u.Host)
+ if err != nil {
+ return nil, err
+ }
+ if host == "" {
+ return nil, fmt.Errorf("missing host")
+ }
+ if port == "" {
+ return nil, fmt.Errorf("missing port")
+ }
+ return u, nil
+}
+
+// This structure is returned by ClientSetup. It consists of a list of method
+// names and the upstream proxy URL, if any.
+type ClientInfo struct {
+ MethodNames []string
+ ProxyURL *url.URL
+}
+
+// Check the client pluggable transports environment, emitting an error message
+// and returning a non-nil error if any error is encountered. Returns a
+// ClientInfo struct.
+//
+// If your program needs to know whether to call ClientSetup or ServerSetup
+// (i.e., if the same program can be run as either a client or a server), check
+// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
+// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
+// // Client mode; call pt.ClientSetup.
+// } else {
+// // Server mode; call pt.ServerSetup.
+// }
+//
+// Always pass nil for the unused single parameter. In the past, the parameter
+// was a list of transport names to use in case Tor requested "*". That feature
+// was never implemented and has been removed from the pluggable transports
+// specification.
+// https://bugs.torproject.org/15612
+func ClientSetup(_ []string) (info ClientInfo, err error) {
+ ver, err := getManagedTransportVer()
+ if err != nil {
+ return
+ }
+ line("VERSION", ver)
+
+ info.MethodNames, err = getClientTransports()
+ if err != nil {
+ return
+ }
+
+ info.ProxyURL, err = getProxyURL()
+ if err != nil {
+ return
+ }
+
+ return info, nil
+}
+
+// A combination of a method name and an address, as extracted from
+// TOR_PT_SERVER_BINDADDR.
+type Bindaddr struct {
+ MethodName string
+ Addr *net.TCPAddr
+ // Options from TOR_PT_SERVER_TRANSPORT_OPTIONS that pertain to this
+ // transport.
+ Options Args
+}
+
+func parsePort(portStr string) (int, error) {
+ port, err := strconv.ParseUint(portStr, 10, 16)
+ return int(port), err
+}
+
+// Resolve an address string into a net.TCPAddr. We are a bit more strict than
+// net.ResolveTCPAddr; we don't allow an empty host or port, and the host part
+// must be a literal IP address.
+func resolveAddr(addrStr string) (*net.TCPAddr, error) {
+ ipStr, portStr, err := net.SplitHostPort(addrStr)
+ if err != nil {
+ // Before the fixing of bug #7011, tor doesn't put brackets around IPv6
+ // addresses. Split after the last colon, assuming it is a port
+ // separator, and try adding the brackets.
+ // https://bugs.torproject.org/7011
+ parts := strings.Split(addrStr, ":")
+ if len(parts) <= 2 {
+ return nil, err
+ }
+ addrStr := "[" + strings.Join(parts[:len(parts)-1], ":") + "]:" + parts[len(parts)-1]
+ ipStr, portStr, err = net.SplitHostPort(addrStr)
+ }
+ if err != nil {
+ return nil, err
+ }
+ if ipStr == "" {
+ return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a host part", addrStr))
+ }
+ if portStr == "" {
+ return nil, net.InvalidAddrError(fmt.Sprintf("address string %q lacks a port part", addrStr))
+ }
+ ip := net.ParseIP(ipStr)
+ if ip == nil {
+ return nil, net.InvalidAddrError(fmt.Sprintf("not an IP string: %q", ipStr))
+ }
+ port, err := parsePort(portStr)
+ if err != nil {
+ return nil, err
+ }
+ return &net.TCPAddr{IP: ip, Port: port}, nil
+}
+
+// Return a new slice, the members of which are those members of addrs having a
+// MethodName in methodNames.
+func filterBindaddrs(addrs []Bindaddr, methodNames []string) []Bindaddr {
+ var result []Bindaddr
+
+ for _, ba := range addrs {
+ for _, methodName := range methodNames {
+ if ba.MethodName == methodName {
+ result = append(result, ba)
+ break
+ }
+ }
+ }
+
+ return result
+}
+
+// Return an array of Bindaddrs, being the contents of TOR_PT_SERVER_BINDADDR
+// with keys filtered by TOR_PT_SERVER_TRANSPORTS. Transport-specific options
+// from TOR_PT_SERVER_TRANSPORT_OPTIONS are assigned to the Options member.
+func getServerBindaddrs() ([]Bindaddr, error) {
+ var result []Bindaddr
+
+ // Parse the list of server transport options.
+ serverTransportOptions := getenv("TOR_PT_SERVER_TRANSPORT_OPTIONS")
+ optionsMap, err := parseServerTransportOptions(serverTransportOptions)
+ if err != nil {
+ return nil, envError(fmt.Sprintf("TOR_PT_SERVER_TRANSPORT_OPTIONS: %q: %s", serverTransportOptions, err.Error()))
+ }
+
+ // Get the list of all requested bindaddrs.
+ serverBindaddr, err := getenvRequired("TOR_PT_SERVER_BINDADDR")
+ if err != nil {
+ return nil, err
+ }
+ seenMethods := make(map[string]bool)
+ for _, spec := range strings.Split(serverBindaddr, ",") {
+ var bindaddr Bindaddr
+
+ parts := strings.SplitN(spec, "-", 2)
+ if len(parts) != 2 {
+ return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: doesn't contain \"-\"", spec))
+ }
+ bindaddr.MethodName = parts[0]
+ // Check for duplicate method names: "Applications MUST NOT set
+ // more than one <address>:<port> pair per PT name."
+ if seenMethods[bindaddr.MethodName] {
+ return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: duplicate method name %q", spec, bindaddr.MethodName))
+ }
+ seenMethods[bindaddr.MethodName] = true
+ addr, err := resolveAddr(parts[1])
+ if err != nil {
+ return nil, envError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: %s", spec, err.Error()))
+ }
+ bindaddr.Addr = addr
+ bindaddr.Options = optionsMap[bindaddr.MethodName]
+ result = append(result, bindaddr)
+ }
+
+ // Filter by TOR_PT_SERVER_TRANSPORTS.
+ serverTransports, err := getenvRequired("TOR_PT_SERVER_TRANSPORTS")
+ if err != nil {
+ return nil, err
+ }
+ result = filterBindaddrs(result, strings.Split(serverTransports, ","))
+
+ return result, nil
+}
+
+func readAuthCookie(f io.Reader) ([]byte, error) {
+ authCookieHeader := []byte("! Extended ORPort Auth Cookie !\x0a")
+ buf := make([]byte, 64)
+
+ n, err := io.ReadFull(f, buf)
+ if err != nil {
+ return nil, err
+ }
+ // Check that the file ends here.
+ n, err = f.Read(make([]byte, 1))
+ if n != 0 {
+ return nil, fmt.Errorf("file is longer than 64 bytes")
+ } else if err != io.EOF {
+ return nil, fmt.Errorf("did not find EOF at end of file")
+ }
+ header := buf[0:32]
+ cookie := buf[32:64]
+ if subtle.ConstantTimeCompare(header, authCookieHeader) != 1 {
+ return nil, fmt.Errorf("missing auth cookie header")
+ }
+
+ return cookie, nil
+}
+
+// Read and validate the contents of an auth cookie file. Returns the 32-byte
+// cookie. See section 4.2.1.2 of 217-ext-orport-auth.txt.
+func readAuthCookieFile(filename string) (cookie []byte, err error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ closeErr := f.Close()
+ if err == nil {
+ err = closeErr
+ }
+ }()
+
+ return readAuthCookie(f)
+}
+
+// This structure is returned by ServerSetup. It consists of a list of
+// Bindaddrs, an address for the ORPort, an address for the extended ORPort (if
+// any), and an authentication cookie (if any).
+type ServerInfo struct {
+ Bindaddrs []Bindaddr
+ OrAddr *net.TCPAddr
+ ExtendedOrAddr *net.TCPAddr
+ AuthCookiePath string
+}
+
+// Check the server pluggable transports environment, emitting an error message
+// and returning a non-nil error if any error is encountered. Resolves the
+// various requested bind addresses, the server ORPort and extended ORPort, and
+// reads the auth cookie file. Returns a ServerInfo struct.
+//
+// If your program needs to know whether to call ClientSetup or ServerSetup
+// (i.e., if the same program can be run as either a client or a server), check
+// whether the TOR_PT_CLIENT_TRANSPORTS environment variable is set:
+// if os.Getenv("TOR_PT_CLIENT_TRANSPORTS") != "" {
+// // Client mode; call pt.ClientSetup.
+// } else {
+// // Server mode; call pt.ServerSetup.
+// }
+//
+// Always pass nil for the unused single parameter. In the past, the parameter
+// was a list of transport names to use in case Tor requested "*". That feature
+// was never implemented and has been removed from the pluggable transports
+// specification.
+// https://bugs.torproject.org/15612
+func ServerSetup(_ []string) (info ServerInfo, err error) {
+ ver, err := getManagedTransportVer()
+ if err != nil {
+ return
+ }
+ line("VERSION", ver)
+
+ info.Bindaddrs, err = getServerBindaddrs()
+ if err != nil {
+ return
+ }
+
+ orPort := getenv("TOR_PT_ORPORT")
+ if orPort != "" {
+ info.OrAddr, err = resolveAddr(orPort)
+ if err != nil {
+ err = envError(fmt.Sprintf("cannot resolve TOR_PT_ORPORT %q: %s", orPort, err.Error()))
+ return
+ }
+ }
+
+ info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE")
+
+ extendedOrPort := getenv("TOR_PT_EXTENDED_SERVER_PORT")
+ if extendedOrPort != "" {
+ if info.AuthCookiePath == "" {
+ err = envError("need TOR_PT_AUTH_COOKIE_FILE environment variable with TOR_PT_EXTENDED_SERVER_PORT")
+ return
+ }
+ info.ExtendedOrAddr, err = resolveAddr(extendedOrPort)
+ if err != nil {
+ err = envError(fmt.Sprintf("cannot resolve TOR_PT_EXTENDED_SERVER_PORT %q: %s", extendedOrPort, err.Error()))
+ return
+ }
+ }
+
+ // Need either OrAddr or ExtendedOrAddr.
+ if info.OrAddr == nil && info.ExtendedOrAddr == nil {
+ err = envError("need TOR_PT_ORPORT or TOR_PT_EXTENDED_SERVER_PORT environment variable")
+ return
+ }
+
+ return info, nil
+}
+
+// See 217-ext-orport-auth.txt section 4.2.1.3.
+func computeServerHash(authCookie, clientNonce, serverNonce []byte) []byte {
+ h := hmac.New(sha256.New, authCookie)
+ io.WriteString(h, "ExtORPort authentication server-to-client hash")
+ h.Write(clientNonce)
+ h.Write(serverNonce)
+ return h.Sum([]byte{})
+}
+
+// See 217-ext-orport-auth.txt section 4.2.1.3.
+func computeClientHash(authCookie, clientNonce, serverNonce []byte) []byte {
+ h := hmac.New(sha256.New, authCookie)
+ io.WriteString(h, "ExtORPort authentication client-to-server hash")
+ h.Write(clientNonce)
+ h.Write(serverNonce)
+ return h.Sum([]byte{})
+}
+
+func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error {
+ // Read auth types. 217-ext-orport-auth.txt section 4.1.
+ var authTypes [256]bool
+ var count int
+ for count = 0; count < 256; count++ {
+ buf := make([]byte, 1)
+ _, err := io.ReadFull(s, buf)
+ if err != nil {
+ return err
+ }
+ b := buf[0]
+ if b == 0 {
+ break
+ }
+ authTypes[b] = true
+ }
+ if count >= 256 {
+ return fmt.Errorf("read 256 auth types without seeing \\x00")
+ }
+
+ // We support only type 1, SAFE_COOKIE.
+ if !authTypes[1] {
+ return fmt.Errorf("server didn't offer auth type 1")
+ }
+ _, err := s.Write([]byte{1})
+ if err != nil {
+ return err
+ }
+
+ clientNonce := make([]byte, 32)
+ clientHash := make([]byte, 32)
+ serverNonce := make([]byte, 32)
+ serverHash := make([]byte, 32)
+
+ _, err = io.ReadFull(rand.Reader, clientNonce)
+ if err != nil {
+ return err
+ }
+ _, err = s.Write(clientNonce)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.ReadFull(s, serverHash)
+ if err != nil {
+ return err
+ }
+ _, err = io.ReadFull(s, serverNonce)
+ if err != nil {
+ return err
+ }
+
+ // Work around tor bug #15240 where the auth cookie is generated after
+ // pluggable transports are launched, leading to a stale cookie getting
+ // cached forever if it is only read once as part of ServerSetup.
+ // https://bugs.torproject.org/15240
+ authCookie, err := readAuthCookieFile(info.AuthCookiePath)
+ if err != nil {
+ return fmt.Errorf("error reading TOR_PT_AUTH_COOKIE_FILE %q: %s", info.AuthCookiePath, err.Error())
+ }
+
+ expectedServerHash := computeServerHash(authCookie, clientNonce, serverNonce)
+ if subtle.ConstantTimeCompare(serverHash, expectedServerHash) != 1 {
+ return fmt.Errorf("mismatch in server hash")
+ }
+
+ clientHash = computeClientHash(authCookie, clientNonce, serverNonce)
+ _, err = s.Write(clientHash)
+ if err != nil {
+ return err
+ }
+
+ status := make([]byte, 1)
+ _, err = io.ReadFull(s, status)
+ if err != nil {
+ return err
+ }
+ if status[0] != 1 {
+ return fmt.Errorf("server rejected authentication")
+ }
+
+ return nil
+}
+
+// See section 3.1.1 of 196-transport-control-ports.txt.
+const (
+ extOrCmdDone = 0x0000
+ extOrCmdUserAddr = 0x0001
+ extOrCmdTransport = 0x0002
+ extOrCmdOkay = 0x1000
+ extOrCmdDeny = 0x1001
+)
+
+func extOrPortSendCommand(s io.Writer, cmd uint16, body []byte) error {
+ var buf bytes.Buffer
+ if len(body) > 65535 {
+ return fmt.Errorf("body length %d exceeds maximum of 65535", len(body))
+ }
+ err := binary.Write(&buf, binary.BigEndian, cmd)
+ if err != nil {
+ return err
+ }
+ err = binary.Write(&buf, binary.BigEndian, uint16(len(body)))
+ if err != nil {
+ return err
+ }
+ err = binary.Write(&buf, binary.BigEndian, body)
+ if err != nil {
+ return err
+ }
+ _, err = s.Write(buf.Bytes())
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Send a USERADDR command on s. See section 3.1.2.1 of
+// 196-transport-control-ports.txt.
+func extOrPortSendUserAddr(s io.Writer, addr string) error {
+ return extOrPortSendCommand(s, extOrCmdUserAddr, []byte(addr))
+}
+
+// Send a TRANSPORT command on s. See section 3.1.2.2 of
+// 196-transport-control-ports.txt.
+func extOrPortSendTransport(s io.Writer, methodName string) error {
+ return extOrPortSendCommand(s, extOrCmdTransport, []byte(methodName))
+}
+
+// Send a DONE command on s. See section 3.1 of 196-transport-control-ports.txt.
+func extOrPortSendDone(s io.Writer) error {
+ return extOrPortSendCommand(s, extOrCmdDone, []byte{})
+}
+
+func extOrPortRecvCommand(s io.Reader) (cmd uint16, body []byte, err error) {
+ var bodyLen uint16
+ data := make([]byte, 4)
+
+ _, err = io.ReadFull(s, data)
+ if err != nil {
+ return
+ }
+ buf := bytes.NewBuffer(data)
+ err = binary.Read(buf, binary.BigEndian, &cmd)
+ if err != nil {
+ return
+ }
+ err = binary.Read(buf, binary.BigEndian, &bodyLen)
+ if err != nil {
+ return
+ }
+ body = make([]byte, bodyLen)
+ _, err = io.ReadFull(s, body)
+ if err != nil {
+ return
+ }
+
+ return cmd, body, err
+}
+
+// Send USERADDR and TRANSPORT commands followed by a DONE command. Wait for an
+// OKAY or DENY response command from the server. If addr or methodName is "",
+// the corresponding command is not sent. Returns nil if and only if OKAY is
+// received.
+func extOrPortSetMetadata(s io.ReadWriter, addr, methodName string) error {
+ var err error
+
+ if addr != "" {
+ err = extOrPortSendUserAddr(s, addr)
+ if err != nil {
+ return err
+ }
+ }
+ if methodName != "" {
+ err = extOrPortSendTransport(s, methodName)
+ if err != nil {
+ return err
+ }
+ }
+ err = extOrPortSendDone(s)
+ if err != nil {
+ return err
+ }
+ cmd, _, err := extOrPortRecvCommand(s)
+ if err != nil {
+ return err
+ }
+ if cmd == extOrCmdDeny {
+ return fmt.Errorf("server returned DENY after our USERADDR and DONE")
+ } else if cmd != extOrCmdOkay {
+ return fmt.Errorf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd)
+ }
+
+ return nil
+}
+
+func extOrPortSetup(s net.Conn, timeout time.Duration,
+ info *ServerInfo, addr, methodName string) error {
+ err := s.SetDeadline(time.Now().Add(5 * time.Second))
+ if err != nil {
+ return err
+ }
+ err = extOrPortAuthenticate(s, info)
+ if err != nil {
+ return err
+ }
+ err = extOrPortSetMetadata(s, addr, methodName)
+ if err != nil {
+ return err
+ }
+ err = s.SetDeadline(time.Time{})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Dial info.ExtendedOrAddr if defined, or else info.OrAddr, and return an open
+// *net.TCPConn. If connecting to the extended OR port, extended OR port
+// authentication à la 217-ext-orport-auth.txt is done before returning; an
+// error is returned if authentication fails.
+//
+// The addr and methodName arguments are put in USERADDR and TRANSPORT ExtOrPort
+// commands, respectively. If either is "", the corresponding command is not
+// sent.
+func DialOr(info *ServerInfo, addr, methodName string) (*net.TCPConn, error) {
+ if info.ExtendedOrAddr == nil || info.AuthCookiePath == "" {
+ return net.DialTCP("tcp", nil, info.OrAddr)
+ }
+
+ s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr)
+ if err != nil {
+ return nil, err
+ }
+ err = extOrPortSetup(s, 5*time.Second, info, addr, methodName)
+ if err != nil {
+ s.Close()
+ return nil, err
+ }
+
+ return s, nil
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go b/vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go
new file mode 100644
index 0000000..29827d9
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/socks.go
@@ -0,0 +1,507 @@
+package pt
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net"
+ "time"
+)
+
+const (
+ socksVersion = 0x05
+
+ socksAuthNoneRequired = 0x00
+ socksAuthUsernamePassword = 0x02
+ socksAuthNoAcceptableMethods = 0xff
+
+ socksCmdConnect = 0x01
+ socksRsv = 0x00
+
+ socksAtypeV4 = 0x01
+ socksAtypeDomainName = 0x03
+ socksAtypeV6 = 0x04
+
+ socksAuthRFC1929Ver = 0x01
+ socksAuthRFC1929Success = 0x00
+ socksAuthRFC1929Fail = 0x01
+
+ socksRepSucceeded = 0x00
+ // "general SOCKS server failure"
+ SocksRepGeneralFailure = 0x01
+ // "connection not allowed by ruleset"
+ SocksRepConnectionNotAllowed = 0x02
+ // "Network unreachable"
+ SocksRepNetworkUnreachable = 0x03
+ // "Host unreachable"
+ SocksRepHostUnreachable = 0x04
+ // "Connection refused"
+ SocksRepConnectionRefused = 0x05
+ // "TTL expired"
+ SocksRepTTLExpired = 0x06
+ // "Command not supported"
+ SocksRepCommandNotSupported = 0x07
+ // "Address type not supported"
+ SocksRepAddressNotSupported = 0x08
+)
+
+// Put a sanity timeout on how long we wait for a SOCKS request.
+const socksRequestTimeout = 5 * time.Second
+
+// SocksRequest describes a SOCKS request.
+type SocksRequest struct {
+ // The endpoint requested by the client as a "host:port" string.
+ Target string
+ // The userid string sent by the client.
+ Username string
+ // The password string sent by the client.
+ Password string
+ // The parsed contents of Username as a key–value mapping.
+ Args Args
+}
+
+// SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
+type SocksConn struct {
+ net.Conn
+ Req SocksRequest
+}
+
+// Send a message to the proxy client that access to the given address is
+// granted. Addr is ignored, and "0.0.0.0:0" is always sent back for
+// BND.ADDR/BND.PORT in the SOCKS response.
+func (conn *SocksConn) Grant(addr *net.TCPAddr) error {
+ return sendSocks5ResponseGranted(conn)
+}
+
+// Send a message to the proxy client that access was rejected or failed. This
+// sends back a "General Failure" error code. RejectReason should be used if
+// more specific error reporting is desired.
+func (conn *SocksConn) Reject() error {
+ return conn.RejectReason(SocksRepGeneralFailure)
+}
+
+// Send a message to the proxy client that access was rejected, with the
+// specific error code indicating the reason behind the rejection.
+func (conn *SocksConn) RejectReason(reason byte) error {
+ return sendSocks5ResponseRejected(conn, reason)
+}
+
+// SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
+//
+// func handleConn(conn *pt.SocksConn) error {
+// defer conn.Close()
+// remote, err := net.Dial("tcp", conn.Req.Target)
+// if err != nil {
+// conn.Reject()
+// return err
+// }
+// defer remote.Close()
+// err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
+// if err != nil {
+// return err
+// }
+// // do something with conn and remote
+// return nil
+// }
+// ...
+// ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
+// if err != nil {
+// panic(err.Error())
+// }
+// for {
+// conn, err := ln.AcceptSocks()
+// if err != nil {
+// log.Printf("accept error: %s", err)
+// if e, ok := err.(net.Error); ok && e.Temporary() {
+// continue
+// }
+// break
+// }
+// go handleConn(conn)
+// }
+type SocksListener struct {
+ net.Listener
+}
+
+// Open a net.Listener according to network and laddr, and return it as a
+// SocksListener.
+func ListenSocks(network, laddr string) (*SocksListener, error) {
+ ln, err := net.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return NewSocksListener(ln), nil
+}
+
+// Create a new SocksListener wrapping the given net.Listener.
+func NewSocksListener(ln net.Listener) *SocksListener {
+ return &SocksListener{ln}
+}
+
+// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
+// It is present for the sake of satisfying the net.Listener interface.
+func (ln *SocksListener) Accept() (net.Conn, error) {
+ return ln.AcceptSocks()
+}
+
+// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
+// SocksConn. After accepting, you must call either conn.Grant or conn.Reject
+// (presumably after trying to connect to conn.Req.Target).
+//
+// Errors returned by AcceptSocks may be temporary (for example, EOF while
+// reading the request, or a badly formatted userid string), or permanent (e.g.,
+// the underlying socket is closed). You can determine whether an error is
+// temporary and take appropriate action with a type conversion to net.Error.
+// For example:
+//
+// for {
+// conn, err := ln.AcceptSocks()
+// if err != nil {
+// if e, ok := err.(net.Error); ok && e.Temporary() {
+// log.Printf("temporary accept error; trying again: %s", err)
+// continue
+// }
+// log.Printf("permanent accept error; giving up: %s", err)
+// break
+// }
+// go handleConn(conn)
+// }
+func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
+retry:
+ c, err := ln.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ conn := new(SocksConn)
+ conn.Conn = c
+ err = conn.SetDeadline(time.Now().Add(socksRequestTimeout))
+ if err != nil {
+ conn.Close()
+ goto retry
+ }
+ conn.Req, err = socks5Handshake(conn)
+ if err != nil {
+ conn.Close()
+ goto retry
+ }
+ err = conn.SetDeadline(time.Time{})
+ if err != nil {
+ conn.Close()
+ goto retry
+ }
+ return conn, nil
+}
+
+// Returns "socks5", suitable to be included in a call to Cmethod.
+func (ln *SocksListener) Version() string {
+ return "socks5"
+}
+
+// socks5handshake conducts the SOCKS5 handshake up to the point where the
+// client command is read and the proxy must open the outgoing connection.
+// Returns a SocksRequest.
+func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) {
+ rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s))
+
+ // Negotiate the authentication method.
+ var method byte
+ if method, err = socksNegotiateAuth(rw); err != nil {
+ return
+ }
+
+ // Authenticate the client.
+ if err = socksAuthenticate(rw, method, &req); err != nil {
+ return
+ }
+
+ // Read the command.
+ err = socksReadCommand(rw, &req)
+ return
+}
+
+// socksNegotiateAuth negotiates the authentication method and returns the
+// selected method as a byte. On negotiation failures an error is returned.
+func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) {
+ // Validate the version.
+ if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
+ return
+ }
+
+ // Read the number of methods.
+ var nmethods byte
+ if nmethods, err = socksReadByte(rw); err != nil {
+ return
+ }
+
+ // Read the methods.
+ var methods []byte
+ if methods, err = socksReadBytes(rw, int(nmethods)); err != nil {
+ return
+ }
+
+ // Pick the most "suitable" method.
+ method = socksAuthNoAcceptableMethods
+ for _, m := range methods {
+ switch m {
+ case socksAuthNoneRequired:
+ // Pick Username/Password over None if the client happens to
+ // send both.
+ if method == socksAuthNoAcceptableMethods {
+ method = m
+ }
+
+ case socksAuthUsernamePassword:
+ method = m
+ }
+ }
+
+ // Send the negotiated method.
+ var msg [2]byte
+ msg[0] = socksVersion
+ msg[1] = method
+ if _, err = rw.Writer.Write(msg[:]); err != nil {
+ return
+ }
+
+ if err = socksFlushBuffers(rw); err != nil {
+ return
+ }
+ return
+}
+
+// socksAuthenticate authenticates the client via the chosen authentication
+// mechanism.
+func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) {
+ switch method {
+ case socksAuthNoneRequired:
+ // Straight into reading the connect.
+
+ case socksAuthUsernamePassword:
+ if err = socksAuthRFC1929(rw, req); err != nil {
+ return
+ }
+
+ case socksAuthNoAcceptableMethods:
+ err = fmt.Errorf("SOCKS method select had no compatible methods")
+ return
+
+ default:
+ err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method)
+ return
+ }
+
+ if err = socksFlushBuffers(rw); err != nil {
+ return
+ }
+ return
+}
+
+// socksAuthRFC1929 authenticates the client via RFC 1929 username/password
+// auth. As a design decision any valid username/password is accepted as this
+// field is primarily used as an out-of-band argument passing mechanism for
+// pluggable transports.
+func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
+ sendErrResp := func() {
+ // Swallow the write/flush error here, we are going to close the
+ // connection and the original failure is more useful.
+ resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail}
+ rw.Write(resp[:])
+ socksFlushBuffers(rw)
+ }
+
+ // Validate the fixed parts of the command message.
+ if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil {
+ sendErrResp()
+ return
+ }
+
+ // Read the username.
+ var ulen byte
+ if ulen, err = socksReadByte(rw); err != nil {
+ return
+ }
+ if ulen < 1 {
+ sendErrResp()
+ err = fmt.Errorf("RFC1929 username with 0 length")
+ return
+ }
+ var uname []byte
+ if uname, err = socksReadBytes(rw, int(ulen)); err != nil {
+ return
+ }
+ req.Username = string(uname)
+
+ // Read the password.
+ var plen byte
+ if plen, err = socksReadByte(rw); err != nil {
+ return
+ }
+ if plen < 1 {
+ sendErrResp()
+ err = fmt.Errorf("RFC1929 password with 0 length")
+ return
+ }
+ var passwd []byte
+ if passwd, err = socksReadBytes(rw, int(plen)); err != nil {
+ return
+ }
+ if !(plen == 1 && passwd[0] == 0x00) {
+ // tor will set the password to 'NUL' if there are no arguments.
+ req.Password = string(passwd)
+ }
+
+ // Mash the username/password together and parse it as a pluggable
+ // transport argument string.
+ if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil {
+ sendErrResp()
+ } else {
+ resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success}
+ _, err = rw.Write(resp[:])
+ }
+ return
+}
+
+// socksReadCommand reads a SOCKS5 client command and parses out the relevant
+// fields into a SocksRequest. Only CMD_CONNECT is supported.
+func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) {
+ sendErrResp := func(reason byte) {
+ // Swallow errors that occur when writing/flushing the response,
+ // connection will be closed anyway.
+ sendSocks5ResponseRejected(rw, reason)
+ socksFlushBuffers(rw)
+ }
+
+ // Validate the fixed parts of the command message.
+ if err = socksReadByteVerify(rw, "version", socksVersion); err != nil {
+ sendErrResp(SocksRepGeneralFailure)
+ return
+ }
+ if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil {
+ sendErrResp(SocksRepCommandNotSupported)
+ return
+ }
+ if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil {
+ sendErrResp(SocksRepGeneralFailure)
+ return
+ }
+
+ // Read the destination address/port.
+ // XXX: This should probably eventually send socks 5 error messages instead
+ // of rudely closing connections on invalid addresses.
+ var atype byte
+ if atype, err = socksReadByte(rw); err != nil {
+ return
+ }
+ var host string
+ switch atype {
+ case socksAtypeV4:
+ var addr []byte
+ if addr, err = socksReadBytes(rw, net.IPv4len); err != nil {
+ return
+ }
+ host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String()
+
+ case socksAtypeDomainName:
+ var alen byte
+ if alen, err = socksReadByte(rw); err != nil {
+ return
+ }
+ if alen == 0 {
+ err = fmt.Errorf("SOCKS request had domain name with 0 length")
+ return
+ }
+ var addr []byte
+ if addr, err = socksReadBytes(rw, int(alen)); err != nil {
+ return
+ }
+ host = string(addr)
+
+ case socksAtypeV6:
+ var rawAddr []byte
+ if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil {
+ return
+ }
+ addr := make(net.IP, net.IPv6len)
+ copy(addr[:], rawAddr[:])
+ host = fmt.Sprintf("[%s]", addr.String())
+
+ default:
+ sendErrResp(SocksRepAddressNotSupported)
+ err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype)
+ return
+ }
+ var rawPort []byte
+ if rawPort, err = socksReadBytes(rw, 2); err != nil {
+ return
+ }
+ port := int(rawPort[0])<<8 | int(rawPort[1])<<0
+
+ if err = socksFlushBuffers(rw); err != nil {
+ return
+ }
+
+ req.Target = fmt.Sprintf("%s:%d", host, port)
+ return
+}
+
+// Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the
+// IPv4 address/port "0.0.0.0:0".
+func sendSocks5Response(w io.Writer, code byte) error {
+ resp := make([]byte, 4+4+2)
+ resp[0] = socksVersion
+ resp[1] = code
+ resp[2] = socksRsv
+ resp[3] = socksAtypeV4
+
+ // BND.ADDR/BND.PORT should be the address and port that the outgoing
+ // connection is bound to on the proxy, but Tor does not use this
+ // information, so all zeroes are sent.
+
+ _, err := w.Write(resp[:])
+ return err
+}
+
+// Send a SOCKS5 response code 0x00.
+func sendSocks5ResponseGranted(w io.Writer) error {
+ return sendSocks5Response(w, socksRepSucceeded)
+}
+
+// Send a SOCKS5 response with the provided failure reason.
+func sendSocks5ResponseRejected(w io.Writer, reason byte) error {
+ return sendSocks5Response(w, reason)
+}
+
+func socksFlushBuffers(rw *bufio.ReadWriter) error {
+ if err := rw.Writer.Flush(); err != nil {
+ return err
+ }
+ if rw.Reader.Buffered() > 0 {
+ return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered())
+ }
+ return nil
+}
+
+func socksReadByte(rw *bufio.ReadWriter) (byte, error) {
+ return rw.Reader.ReadByte()
+}
+
+func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) {
+ ret := make([]byte, n)
+ if _, err := io.ReadFull(rw.Reader, ret); err != nil {
+ return nil, err
+ }
+ return ret, nil
+}
+
+func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error {
+ val, err := socksReadByte(rw)
+ if err != nil {
+ return err
+ }
+ if val != expected {
+ return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected)
+ }
+ return nil
+}
+
+var _ net.Listener = (*SocksListener)(nil)
diff --git a/vendor/git.torproject.org/pluggable-transports/goptlib.git/test_authcookie b/vendor/git.torproject.org/pluggable-transports/goptlib.git/test_authcookie
new file mode 100644
index 0000000..0df0cfb
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/goptlib.git/test_authcookie
@@ -0,0 +1,2 @@
+! Extended ORPort Auth Cookie !
+this file is used in test code.
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/LICENSE b/vendor/git.torproject.org/pluggable-transports/snowflake.git/LICENSE
new file mode 100644
index 0000000..42f6296
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/LICENSE
@@ -0,0 +1,32 @@
+ This file contains the license for "Snowflake"
+ a free software project which provides a WebRTC pluggable transport.
+
+================================================================================
+Copyright (c) 2016, Serene Han, Arlo Breault
+Copyright (c) 2019-2020, The Tor Project, Inc
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+================================================================================
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/encapsulation/encapsulation.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/encapsulation/encapsulation.go
new file mode 100644
index 0000000..bfe9b5b
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/encapsulation/encapsulation.go
@@ -0,0 +1,194 @@
+// Package encapsulation implements a way of encoding variable-size chunks of
+// data and padding into a byte stream.
+//
+// Each chunk of data or padding starts with a variable-size length prefix. One
+// bit ("d") in the first byte of the prefix indicates whether the chunk
+// represents data or padding (1=data, 0=padding). Another bit ("c" for
+// "continuation") is the indicates whether there are more bytes in the length
+// prefix. The remaining 6 bits ("x") encode part of the length value.
+// dcxxxxxx
+// If the continuation bit is set, then the next byte is also part of the length
+// prefix. It lacks the "d" bit, has its own "c" bit, and 7 value-carrying bits
+// ("y").
+// cyyyyyyy
+// The length is decoded by concatenating value-carrying bits, from left to
+// right, of all value-carrying bits, up to and including the first byte whose
+// "c" bit is 0. Although in principle this encoding would allow for length
+// prefixes of any size, length prefixes are arbitrarily limited to 3 bytes and
+// any attempt to read or write a longer one is an error. These are therefore
+// the only valid formats:
+// 00xxxxxx xxxxxx₂ bytes of padding
+// 10xxxxxx xxxxxx₂ bytes of data
+// 01xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of padding
+// 11xxxxxx 0yyyyyyy xxxxxxyyyyyyy₂ bytes of data
+// 01xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of padding
+// 11xxxxxx 1yyyyyyy 0zzzzzzz xxxxxxyyyyyyyzzzzzzz₂ bytes of data
+// The maximum encodable length is 11111111111111111111₂ = 0xfffff = 1048575.
+// There is no requirement to use a length prefix of minimum size; i.e. 00000100
+// and 01000000 00000100 are both valid encodings of the value 4.
+//
+// After the length prefix follow that many bytes of padding or data. There are
+// no restrictions on the value of bytes comprising padding.
+//
+// The idea for this encapsulation is sketched here:
+// https://github.com/net4people/bbs/issues/9#issuecomment-524095186
+package encapsulation
+
+import (
+ "errors"
+ "io"
+ "io/ioutil"
+)
+
+// ErrTooLong is the error returned when an encoded length prefix is longer than
+// 3 bytes, or when ReadData receives an input whose length is too large to
+// encode in a 3-byte length prefix.
+var ErrTooLong = errors.New("length prefix is too long")
+
+// ReadData returns a new slice with the contents of the next available data
+// chunk, skipping over any padding chunks that may come first. The returned
+// error value is nil if and only if a data chunk was present and was read in
+// its entirety. The returned error is io.EOF only if r ended before the first
+// byte of a length prefix. If r ended in the middle of a length prefix or
+// data/padding, the returned error is io.ErrUnexpectedEOF.
+func ReadData(r io.Reader) ([]byte, error) {
+ for {
+ var b [1]byte
+ _, err := r.Read(b[:])
+ if err != nil {
+ // This is the only place we may return a real io.EOF.
+ return nil, err
+ }
+ isData := (b[0] & 0x80) != 0
+ moreLength := (b[0] & 0x40) != 0
+ n := int(b[0] & 0x3f)
+ for i := 0; moreLength; i++ {
+ if i >= 2 {
+ return nil, ErrTooLong
+ }
+ _, err := r.Read(b[:])
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ if err != nil {
+ return nil, err
+ }
+ moreLength = (b[0] & 0x80) != 0
+ n = (n << 7) | int(b[0]&0x7f)
+ }
+ if isData {
+ p := make([]byte, n)
+ _, err := io.ReadFull(r, p)
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ if err != nil {
+ return nil, err
+ }
+ return p, err
+ } else {
+ _, err := io.CopyN(ioutil.Discard, r, int64(n))
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+}
+
+// dataPrefixForLength returns a length prefix for the given length, with the
+// "d" bit set to 1.
+func dataPrefixForLength(n int) ([]byte, error) {
+ switch {
+ case (n>>0)&0x3f == (n >> 0):
+ return []byte{0x80 | byte((n>>0)&0x3f)}, nil
+ case (n>>7)&0x3f == (n >> 7):
+ return []byte{0xc0 | byte((n>>7)&0x3f), byte((n >> 0) & 0x7f)}, nil
+ case (n>>14)&0x3f == (n >> 14):
+ return []byte{0xc0 | byte((n>>14)&0x3f), 0x80 | byte((n>>7)&0x7f), byte((n >> 0) & 0x7f)}, nil
+ default:
+ return nil, ErrTooLong
+ }
+}
+
+// WriteData encodes a data chunk into w. It returns the total number of bytes
+// written; i.e., including the length prefix. The error is ErrTooLong if the
+// length of data cannot fit into a length prefix.
+func WriteData(w io.Writer, data []byte) (int, error) {
+ prefix, err := dataPrefixForLength(len(data))
+ if err != nil {
+ return 0, err
+ }
+ total := 0
+ n, err := w.Write(prefix)
+ total += n
+ if err != nil {
+ return total, err
+ }
+ n, err = w.Write(data)
+ total += n
+ return total, err
+}
+
+var paddingBuffer = make([]byte, 1024)
+
+// WritePadding encodes padding chunks, whose total size (including their own
+// length prefixes) is n. Returns the total number of bytes written to w, which
+// will be exactly n unless there was an error. The error cannot be ErrTooLong
+// because this function will write multiple padding chunks if necessary to
+// reach the requested size. Panics if n is negative.
+func WritePadding(w io.Writer, n int) (int, error) {
+ if n < 0 {
+ panic("negative length")
+ }
+ total := 0
+ for n > 0 {
+ p := len(paddingBuffer)
+ if p > n {
+ p = n
+ }
+ n -= p
+ var prefix []byte
+ switch {
+ case ((p-1)>>0)&0x3f == ((p - 1) >> 0):
+ p = p - 1
+ prefix = []byte{byte((p >> 0) & 0x3f)}
+ case ((p-2)>>7)&0x3f == ((p - 2) >> 7):
+ p = p - 2
+ prefix = []byte{0x40 | byte((p>>7)&0x3f), byte((p >> 0) & 0x7f)}
+ case ((p-3)>>14)&0x3f == ((p - 3) >> 14):
+ p = p - 3
+ prefix = []byte{0x40 | byte((p>>14)&0x3f), 0x80 | byte((p>>7)&0x3f), byte((p >> 0) & 0x7f)}
+ }
+ nn, err := w.Write(prefix)
+ total += nn
+ if err != nil {
+ return total, err
+ }
+ nn, err = w.Write(paddingBuffer[:p])
+ total += nn
+ if err != nil {
+ return total, err
+ }
+ }
+ return total, nil
+}
+
+// MaxDataForSize returns the length of the longest slice that can pe passed to
+// WriteData, whose total encoded size (including length prefix) is no larger
+// than n. Call this to find out if a chunk of data will fit into a length
+// budget. Panics if n == 0.
+func MaxDataForSize(n int) int {
+ if n == 0 {
+ panic("zero length")
+ }
+ prefix, err := dataPrefixForLength(n)
+ if err == ErrTooLong {
+ return (1 << (6 + 7 + 7)) - 1 - 3
+ } else if err != nil {
+ panic(err)
+ }
+ return n - len(prefix)
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/nat/nat.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/nat/nat.go
new file mode 100644
index 0000000..552ed45
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/nat/nat.go
@@ -0,0 +1,246 @@
+/*
+The majority of this code is taken from a utility I wrote for pion/stun
+https://github.com/pion/stun/blob/master/cmd/stun-nat-behaviour/main.go
+
+Copyright 2018 Pion LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package nat
+
+import (
+ "errors"
+ "fmt"
+ "log"
+ "net"
+ "time"
+
+ "github.com/pion/stun"
+)
+
+var ErrTimedOut = errors.New("timed out waiting for response")
+
+const (
+ NATUnknown = "unknown"
+ NATRestricted = "restricted"
+ NATUnrestricted = "unrestricted"
+)
+
+// This function checks the NAT mapping and filtering
+// behaviour and returns true if the NAT is restrictive
+// (address-dependent mapping and/or port-dependent filtering)
+// and false if the NAT is unrestrictive (meaning it
+// will work with most other NATs),
+func CheckIfRestrictedNAT(server string) (bool, error) {
+ return isRestrictedMapping(server)
+}
+
+// Performs two tests from RFC 5780 to determine whether the mapping type
+// of the client's NAT is address-independent or address-dependent
+// Returns true if the mapping is address-dependent and false otherwise
+func isRestrictedMapping(addrStr string) (bool, error) {
+ var xorAddr1 stun.XORMappedAddress
+ var xorAddr2 stun.XORMappedAddress
+
+ mapTestConn, err := connect(addrStr)
+ if err != nil {
+ log.Printf("Error creating STUN connection: %s", err.Error())
+ return false, err
+ }
+
+ defer mapTestConn.Close()
+
+ // Test I: Regular binding request
+ message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
+
+ resp, err := mapTestConn.RoundTrip(message, mapTestConn.PrimaryAddr)
+ if err == ErrTimedOut {
+ log.Printf("Error: no response from server")
+ return false, err
+ }
+ if err != nil {
+ log.Printf("Error receiving response from server: %s", err.Error())
+ return false, err
+ }
+
+ // Decoding XOR-MAPPED-ADDRESS attribute from message.
+ if err = xorAddr1.GetFrom(resp); err != nil {
+ log.Printf("Error retrieving XOR-MAPPED-ADDRESS resonse: %s", err.Error())
+ return false, err
+ }
+
+ // Decoding OTHER-ADDRESS attribute from message.
+ var otherAddr stun.OtherAddress
+ if err = otherAddr.GetFrom(resp); err != nil {
+ log.Println("NAT discovery feature not supported by this server")
+ return false, err
+ }
+
+ if err = mapTestConn.AddOtherAddr(otherAddr.String()); err != nil {
+ log.Printf("Failed to resolve address %s\t", otherAddr.String())
+ return false, err
+ }
+
+ // Test II: Send binding request to other address
+ resp, err = mapTestConn.RoundTrip(message, mapTestConn.OtherAddr)
+ if err == ErrTimedOut {
+ log.Printf("Error: no response from server")
+ return false, err
+ }
+ if err != nil {
+ log.Printf("Error retrieving server response: %s", err.Error())
+ return false, err
+ }
+
+ // Decoding XOR-MAPPED-ADDRESS attribute from message.
+ if err = xorAddr2.GetFrom(resp); err != nil {
+ log.Printf("Error retrieving XOR-MAPPED-ADDRESS resonse: %s", err.Error())
+ return false, err
+ }
+
+ return xorAddr1.String() != xorAddr2.String(), nil
+
+}
+
+// Performs two tests from RFC 5780 to determine whether the filtering type
+// of the client's NAT is port-dependent.
+// Returns true if the filtering is port-dependent and false otherwise
+// Note: This function is no longer used because a client's NAT type is
+// determined only by their mapping type, but the functionality might
+// be useful in the future and remains here.
+func isRestrictedFiltering(addrStr string) (bool, error) {
+ var xorAddr stun.XORMappedAddress
+
+ mapTestConn, err := connect(addrStr)
+ if err != nil {
+ log.Printf("Error creating STUN connection: %s", err.Error())
+ return false, err
+ }
+
+ defer mapTestConn.Close()
+
+ // Test I: Regular binding request
+ message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
+
+ resp, err := mapTestConn.RoundTrip(message, mapTestConn.PrimaryAddr)
+ if err == ErrTimedOut {
+ log.Printf("Error: no response from server")
+ return false, err
+ }
+ if err != nil {
+ log.Printf("Error: %s", err.Error())
+ return false, err
+ }
+
+ // Decoding XOR-MAPPED-ADDRESS attribute from message.
+ if err = xorAddr.GetFrom(resp); err != nil {
+ log.Printf("Error retrieving XOR-MAPPED-ADDRESS from resonse: %s", err.Error())
+ return false, err
+ }
+
+ // Test III: Request port change
+ message.Add(stun.AttrChangeRequest, []byte{0x00, 0x00, 0x00, 0x02})
+
+ _, err = mapTestConn.RoundTrip(message, mapTestConn.PrimaryAddr)
+ if err != ErrTimedOut && err != nil {
+ // something else went wrong
+ log.Printf("Error reading response from server: %s", err.Error())
+ return false, err
+ }
+
+ return err == ErrTimedOut, nil
+}
+
+// Given an address string, returns a StunServerConn
+func connect(addrStr string) (*StunServerConn, error) {
+ // Creating a "connection" to STUN server.
+ addr, err := net.ResolveUDPAddr("udp4", addrStr)
+ if err != nil {
+ log.Printf("Error resolving address: %s\n", err.Error())
+ return nil, err
+ }
+
+ c, err := net.ListenUDP("udp4", nil)
+ if err != nil {
+ return nil, err
+ }
+
+ mChan := listen(c)
+
+ return &StunServerConn{
+ conn: c,
+ PrimaryAddr: addr,
+ messageChan: mChan,
+ }, nil
+}
+
+type StunServerConn struct {
+ conn net.PacketConn
+ PrimaryAddr *net.UDPAddr
+ OtherAddr *net.UDPAddr
+ messageChan chan *stun.Message
+}
+
+func (c *StunServerConn) Close() {
+ c.conn.Close()
+}
+
+func (c *StunServerConn) RoundTrip(msg *stun.Message, addr net.Addr) (*stun.Message, error) {
+ _, err := c.conn.WriteTo(msg.Raw, addr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Wait for response or timeout
+ select {
+ case m, ok := <-c.messageChan:
+ if !ok {
+ return nil, fmt.Errorf("error reading from messageChan")
+ }
+ return m, nil
+ case <-time.After(10 * time.Second):
+ return nil, ErrTimedOut
+ }
+}
+
+func (c *StunServerConn) AddOtherAddr(addrStr string) error {
+ addr2, err := net.ResolveUDPAddr("udp4", addrStr)
+ if err != nil {
+ return err
+ }
+ c.OtherAddr = addr2
+ return nil
+}
+
+// taken from https://github.com/pion/stun/blob/master/cmd/stun-traversal/main.go
+func listen(conn *net.UDPConn) chan *stun.Message {
+ messages := make(chan *stun.Message)
+ go func() {
+ for {
+ buf := make([]byte, 1024)
+
+ n, _, err := conn.ReadFromUDP(buf)
+ if err != nil {
+ close(messages)
+ return
+ }
+ buf = buf[:n]
+
+ m := new(stun.Message)
+ m.Raw = buf
+ err = m.Decode()
+ if err != nil {
+ close(messages)
+ return
+ }
+
+ messages <- m
+ }
+ }()
+ return messages
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/safelog/log.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/safelog/log.go
new file mode 100644
index 0000000..9148e53
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/safelog/log.go
@@ -0,0 +1,71 @@
+//Package for a safer logging wrapper around the standard logging package
+
+//import "git.torproject.org/pluggable-transports/snowflake.git/common/safelog"
+package safelog
+
+import (
+ "bytes"
+ "io"
+ "regexp"
+ "sync"
+)
+
+const ipv4Address = `\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`
+const ipv6Address = `([0-9a-fA-F]{0,4}:){5,7}([0-9a-fA-F]{0,4})?`
+const ipv6Compressed = `([0-9a-fA-F]{0,4}:){0,5}([0-9a-fA-F]{0,4})?(::)([0-9a-fA-F]{0,4}:){0,5}([0-9a-fA-F]{0,4})?`
+const ipv6Full = `(` + ipv6Address + `(` + ipv4Address + `))` +
+ `|(` + ipv6Compressed + `(` + ipv4Address + `))` +
+ `|(` + ipv6Address + `)` + `|(` + ipv6Compressed + `)`
+const optionalPort = `(:\d{1,5})?`
+const addressPattern = `((` + ipv4Address + `)|(\[(` + ipv6Full + `)\])|(` + ipv6Full + `))` + optionalPort
+const fullAddrPattern = `(^|\s|[^\w:])` + addressPattern + `(\s|(:\s)|[^\w:]|$)`
+
+var scrubberPatterns = []*regexp.Regexp{
+ regexp.MustCompile(fullAddrPattern),
+}
+
+var addressRegexp = regexp.MustCompile(addressPattern)
+
+// An io.Writer that can be used as the output for a logger that first
+// sanitizes logs and then writes to the provided io.Writer
+type LogScrubber struct {
+ Output io.Writer
+ buffer []byte
+
+ lock sync.Mutex
+}
+
+func (ls *LogScrubber) Lock() { (*ls).lock.Lock() }
+func (ls *LogScrubber) Unlock() { (*ls).lock.Unlock() }
+
+func scrub(b []byte) []byte {
+ scrubbedBytes := b
+ for _, pattern := range scrubberPatterns {
+ // this is a workaround since go does not yet support look ahead or look
+ // behind for regular expressions.
+ scrubbedBytes = pattern.ReplaceAllFunc(scrubbedBytes, func(b []byte) []byte {
+ return addressRegexp.ReplaceAll(b, []byte("[scrubbed]"))
+ })
+ }
+ return scrubbedBytes
+}
+
+func (ls *LogScrubber) Write(b []byte) (n int, err error) {
+ ls.Lock()
+ defer ls.Unlock()
+
+ n = len(b)
+ ls.buffer = append(ls.buffer, b...)
+ for {
+ i := bytes.LastIndexByte(ls.buffer, '\n')
+ if i == -1 {
+ return
+ }
+ fullLines := ls.buffer[:i+1]
+ _, err = ls.Output.Write(scrub(fullLines))
+ if err != nil {
+ return
+ }
+ ls.buffer = ls.buffer[i+1:]
+ }
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientid.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientid.go
new file mode 100644
index 0000000..17257e1
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientid.go
@@ -0,0 +1,28 @@
+package turbotunnel
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+)
+
+// ClientID is an abstract identifier that binds together all the communications
+// belonging to a single client session, even though those communications may
+// arrive from multiple IP addresses or over multiple lower-level connections.
+// It plays the same role that an (IP address, port number) tuple plays in a
+// net.UDPConn: it's the return address pertaining to a long-lived abstract
+// client session. The client attaches its ClientID to each of its
+// communications, enabling the server to disambiguate requests among its many
+// clients. ClientID implements the net.Addr interface.
+type ClientID [8]byte
+
+func NewClientID() ClientID {
+ var id ClientID
+ _, err := rand.Read(id[:])
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+func (id ClientID) Network() string { return "clientid" }
+func (id ClientID) String() string { return hex.EncodeToString(id[:]) }
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientmap.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientmap.go
new file mode 100644
index 0000000..53d0302
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/clientmap.go
@@ -0,0 +1,145 @@
+package turbotunnel
+
+import (
+ "container/heap"
+ "net"
+ "sync"
+ "time"
+)
+
+// clientRecord is a record of a recently seen client, with the time it was last
+// seen and a send queue.
+type clientRecord struct {
+ Addr net.Addr
+ LastSeen time.Time
+ SendQueue chan []byte
+}
+
+// ClientMap manages a mapping of live clients (keyed by address, which will be
+// a ClientID) to their respective send queues. ClientMap's functions are safe
+// to call from multiple goroutines.
+type ClientMap struct {
+ // We use an inner structure to avoid exposing public heap.Interface
+ // functions to users of clientMap.
+ inner clientMapInner
+ // Synchronizes access to inner.
+ lock sync.Mutex
+}
+
+// NewClientMap creates a ClientMap that expires clients after a timeout.
+//
+// The timeout does not have to be kept in sync with QUIC's internal idle
+// timeout. If a client is removed from the client map while the QUIC session is
+// still live, the worst that can happen is a loss of whatever packets were in
+// the send queue at the time. If QUIC later decides to send more packets to the
+// same client, we'll instantiate a new send queue, and if the client ever
+// connects again with the proper client ID, we'll deliver them.
+func NewClientMap(timeout time.Duration) *ClientMap {
+ m := &ClientMap{
+ inner: clientMapInner{
+ byAge: make([]*clientRecord, 0),
+ byAddr: make(map[net.Addr]int),
+ },
+ }
+ go func() {
+ for {
+ time.Sleep(timeout / 2)
+ now := time.Now()
+ m.lock.Lock()
+ m.inner.removeExpired(now, timeout)
+ m.lock.Unlock()
+ }
+ }()
+ return m
+}
+
+// SendQueue returns the send queue corresponding to addr, creating it if
+// necessary.
+func (m *ClientMap) SendQueue(addr net.Addr) chan []byte {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ return m.inner.SendQueue(addr, time.Now())
+}
+
+// clientMapInner is the inner type of ClientMap, implementing heap.Interface.
+// byAge is the backing store, a heap ordered by LastSeen time, to facilitate
+// expiring old client records. byAddr is a map from addresses (i.e., ClientIDs)
+// to heap indices, to allow looking up by address. Unlike ClientMap,
+// clientMapInner requires external synchonization.
+type clientMapInner struct {
+ byAge []*clientRecord
+ byAddr map[net.Addr]int
+}
+
+// removeExpired removes all client records whose LastSeen timestamp is more
+// than timeout in the past.
+func (inner *clientMapInner) removeExpired(now time.Time, timeout time.Duration) {
+ for len(inner.byAge) > 0 && now.Sub(inner.byAge[0].LastSeen) >= timeout {
+ heap.Pop(inner)
+ }
+}
+
+// SendQueue finds the existing client record corresponding to addr, or creates
+// a new one if none exists yet. It updates the client record's LastSeen time
+// and returns its SendQueue.
+func (inner *clientMapInner) SendQueue(addr net.Addr, now time.Time) chan []byte {
+ var record *clientRecord
+ i, ok := inner.byAddr[addr]
+ if ok {
+ // Found one, update its LastSeen.
+ record = inner.byAge[i]
+ record.LastSeen = now
+ heap.Fix(inner, i)
+ } else {
+ // Not found, create a new one.
+ record = &clientRecord{
+ Addr: addr,
+ LastSeen: now,
+ SendQueue: make(chan []byte, queueSize),
+ }
+ heap.Push(inner, record)
+ }
+ return record.SendQueue
+}
+
+// heap.Interface for clientMapInner.
+
+func (inner *clientMapInner) Len() int {
+ if len(inner.byAge) != len(inner.byAddr) {
+ panic("inconsistent clientMap")
+ }
+ return len(inner.byAge)
+}
+
+func (inner *clientMapInner) Less(i, j int) bool {
+ return inner.byAge[i].LastSeen.Before(inner.byAge[j].LastSeen)
+}
+
+func (inner *clientMapInner) Swap(i, j int) {
+ inner.byAge[i], inner.byAge[j] = inner.byAge[j], inner.byAge[i]
+ inner.byAddr[inner.byAge[i].Addr] = i
+ inner.byAddr[inner.byAge[j].Addr] = j
+}
+
+func (inner *clientMapInner) Push(x interface{}) {
+ record := x.(*clientRecord)
+ if _, ok := inner.byAddr[record.Addr]; ok {
+ panic("duplicate address in clientMap")
+ }
+ // Insert into byAddr map.
+ inner.byAddr[record.Addr] = len(inner.byAge)
+ // Insert into byAge slice.
+ inner.byAge = append(inner.byAge, record)
+}
+
+func (inner *clientMapInner) Pop() interface{} {
+ n := len(inner.byAddr)
+ // Remove from byAge slice.
+ record := inner.byAge[n-1]
+ inner.byAge[n-1] = nil
+ inner.byAge = inner.byAge[:n-1]
+ // Remove from byAddr map.
+ delete(inner.byAddr, record.Addr)
+ close(record.SendQueue)
+ return record
+}
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/consts.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/consts.go
new file mode 100644
index 0000000..80f70af
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/consts.go
@@ -0,0 +1,17 @@
+// Package turbotunnel provides support for overlaying a virtual net.PacketConn
+// on some other network carrier.
+//
+// https://github.com/net4people/bbs/issues/9
+package turbotunnel
+
+import "errors"
+
+// This magic prefix is how a client opts into turbo tunnel mode. It is just a
+// randomly generated byte string.
+var Token = [8]byte{0x12, 0x93, 0x60, 0x5d, 0x27, 0x81, 0x75, 0xf5}
+
+// The size of receive and send queues.
+const queueSize = 32
+
+var errClosedPacketConn = errors.New("operation on closed connection")
+var errNotImplemented = errors.New("not implemented")
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/queuepacketconn.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/queuepacketconn.go
new file mode 100644
index 0000000..14a9833
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/queuepacketconn.go
@@ -0,0 +1,137 @@
+package turbotunnel
+
+import (
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// taggedPacket is a combination of a []byte and a net.Addr, encapsulating the
+// return type of PacketConn.ReadFrom.
+type taggedPacket struct {
+ P []byte
+ Addr net.Addr
+}
+
+// QueuePacketConn implements net.PacketConn by storing queues of packets. There
+// is one incoming queue (where packets are additionally tagged by the source
+// address of the client that sent them). There are many outgoing queues, one
+// for each client address that has been recently seen. The QueueIncoming method
+// inserts a packet into the incoming queue, to eventually be returned by
+// ReadFrom. WriteTo inserts a packet into an address-specific outgoing queue,
+// which can later by accessed through the OutgoingQueue method.
+type QueuePacketConn struct {
+ clients *ClientMap
+ localAddr net.Addr
+ recvQueue chan taggedPacket
+ closeOnce sync.Once
+ closed chan struct{}
+ // What error to return when the QueuePacketConn is closed.
+ err atomic.Value
+}
+
+// NewQueuePacketConn makes a new QueuePacketConn, set to track recent clients
+// for at least a duration of timeout.
+func NewQueuePacketConn(localAddr net.Addr, timeout time.Duration) *QueuePacketConn {
+ return &QueuePacketConn{
+ clients: NewClientMap(timeout),
+ localAddr: localAddr,
+ recvQueue: make(chan taggedPacket, queueSize),
+ closed: make(chan struct{}),
+ }
+}
+
+// QueueIncoming queues and incoming packet and its source address, to be
+// returned in a future call to ReadFrom.
+func (c *QueuePacketConn) QueueIncoming(p []byte, addr net.Addr) {
+ select {
+ case <-c.closed:
+ // If we're closed, silently drop it.
+ return
+ default:
+ }
+ // Copy the slice so that the caller may reuse it.
+ buf := make([]byte, len(p))
+ copy(buf, p)
+ select {
+ case c.recvQueue <- taggedPacket{buf, addr}:
+ default:
+ // Drop the incoming packet if the receive queue is full.
+ }
+}
+
+// OutgoingQueue returns the queue of outgoing packets corresponding to addr,
+// creating it if necessary. The contents of the queue will be packets that are
+// written to the address in question using WriteTo.
+func (c *QueuePacketConn) OutgoingQueue(addr net.Addr) <-chan []byte {
+ return c.clients.SendQueue(addr)
+}
+
+// ReadFrom returns a packet and address previously stored by QueueIncoming.
+func (c *QueuePacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
+ select {
+ case <-c.closed:
+ return 0, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)}
+ default:
+ }
+ select {
+ case <-c.closed:
+ return 0, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)}
+ case packet := <-c.recvQueue:
+ return copy(p, packet.P), packet.Addr, nil
+ }
+}
+
+// WriteTo queues an outgoing packet for the given address. The queue can later
+// be retrieved using the OutgoingQueue method.
+func (c *QueuePacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
+ select {
+ case <-c.closed:
+ return 0, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)}
+ default:
+ }
+ // Copy the slice so that the caller may reuse it.
+ buf := make([]byte, len(p))
+ copy(buf, p)
+ select {
+ case c.clients.SendQueue(addr) <- buf:
+ return len(buf), nil
+ default:
+ // Drop the outgoing packet if the send queue is full.
+ return len(buf), nil
+ }
+}
+
+// closeWithError unblocks pending operations and makes future operations fail
+// with the given error. If err is nil, it becomes errClosedPacketConn.
+func (c *QueuePacketConn) closeWithError(err error) error {
+ var newlyClosed bool
+ c.closeOnce.Do(func() {
+ newlyClosed = true
+ // Store the error to be returned by future PacketConn
+ // operations.
+ if err == nil {
+ err = errClosedPacketConn
+ }
+ c.err.Store(err)
+ close(c.closed)
+ })
+ if !newlyClosed {
+ return &net.OpError{Op: "close", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)}
+ }
+ return nil
+}
+
+// Close unblocks pending operations and makes future operations fail with a
+// "closed connection" error.
+func (c *QueuePacketConn) Close() error {
+ return c.closeWithError(nil)
+}
+
+// LocalAddr returns the localAddr value that was passed to NewQueuePacketConn.
+func (c *QueuePacketConn) LocalAddr() net.Addr { return c.localAddr }
+
+func (c *QueuePacketConn) SetDeadline(t time.Time) error { return errNotImplemented }
+func (c *QueuePacketConn) SetReadDeadline(t time.Time) error { return errNotImplemented }
+func (c *QueuePacketConn) SetWriteDeadline(t time.Time) error { return errNotImplemented }
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/redialpacketconn.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/redialpacketconn.go
new file mode 100644
index 0000000..cf6a8c9
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/turbotunnel/redialpacketconn.go
@@ -0,0 +1,204 @@
+package turbotunnel
+
+import (
+ "context"
+ "errors"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// RedialPacketConn implements a long-lived net.PacketConn atop a sequence of
+// other, transient net.PacketConns. RedialPacketConn creates a new
+// net.PacketConn by calling a provided dialContext function. Whenever the
+// net.PacketConn experiences a ReadFrom or WriteTo error, RedialPacketConn
+// calls the dialContext function again and starts sending and receiving packets
+// on the new net.PacketConn. RedialPacketConn's own ReadFrom and WriteTo
+// methods return an error only when the dialContext function returns an error.
+//
+// RedialPacketConn uses static local and remote addresses that are independent
+// of those of any dialed net.PacketConn.
+type RedialPacketConn struct {
+ localAddr net.Addr
+ remoteAddr net.Addr
+ dialContext func(context.Context) (net.PacketConn, error)
+ recvQueue chan []byte
+ sendQueue chan []byte
+ closed chan struct{}
+ closeOnce sync.Once
+ // The first dial error, which causes the clientPacketConn to be
+ // closed and is returned from future read/write operations. Compare to
+ // the rerr and werr in io.Pipe.
+ err atomic.Value
+}
+
+// NewQueuePacketConn makes a new RedialPacketConn, with the given static local
+// and remote addresses, and dialContext function.
+func NewRedialPacketConn(
+ localAddr, remoteAddr net.Addr,
+ dialContext func(context.Context) (net.PacketConn, error),
+) *RedialPacketConn {
+ c := &RedialPacketConn{
+ localAddr: localAddr,
+ remoteAddr: remoteAddr,
+ dialContext: dialContext,
+ recvQueue: make(chan []byte, queueSize),
+ sendQueue: make(chan []byte, queueSize),
+ closed: make(chan struct{}),
+ err: atomic.Value{},
+ }
+ go c.dialLoop()
+ return c
+}
+
+// dialLoop repeatedly calls c.dialContext and passes the resulting
+// net.PacketConn to c.exchange. It returns only when c is closed or dialContext
+// returns an error.
+func (c *RedialPacketConn) dialLoop() {
+ ctx, cancel := context.WithCancel(context.Background())
+ for {
+ select {
+ case <-c.closed:
+ cancel()
+ return
+ default:
+ }
+ conn, err := c.dialContext(ctx)
+ if err != nil {
+ c.closeWithError(err)
+ cancel()
+ return
+ }
+ c.exchange(conn)
+ conn.Close()
+ }
+}
+
+// exchange calls ReadFrom on the given net.PacketConn and places the resulting
+// packets in the receive queue, and takes packets from the send queue and calls
+// WriteTo on them, making the current net.PacketConn active.
+func (c *RedialPacketConn) exchange(conn net.PacketConn) {
+ readErrCh := make(chan error)
+ writeErrCh := make(chan error)
+
+ go func() {
+ defer close(readErrCh)
+ for {
+ select {
+ case <-c.closed:
+ return
+ case <-writeErrCh:
+ return
+ default:
+ }
+
+ var buf [1500]byte
+ n, _, err := conn.ReadFrom(buf[:])
+ if err != nil {
+ readErrCh <- err
+ return
+ }
+ p := make([]byte, n)
+ copy(p, buf[:])
+ select {
+ case c.recvQueue <- p:
+ default: // OK to drop packets.
+ }
+ }
+ }()
+
+ go func() {
+ defer close(writeErrCh)
+ for {
+ select {
+ case <-c.closed:
+ return
+ case <-readErrCh:
+ return
+ case p := <-c.sendQueue:
+ _, err := conn.WriteTo(p, c.remoteAddr)
+ if err != nil {
+ writeErrCh <- err
+ return
+ }
+ }
+ }
+ }()
+
+ select {
+ case <-readErrCh:
+ case <-writeErrCh:
+ }
+}
+
+// ReadFrom reads a packet from the currently active net.PacketConn. The
+// packet's original remote address is replaced with the RedialPacketConn's own
+// remote address.
+func (c *RedialPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
+ select {
+ case <-c.closed:
+ return 0, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: c.remoteAddr, Err: c.err.Load().(error)}
+ default:
+ }
+ select {
+ case <-c.closed:
+ return 0, nil, &net.OpError{Op: "read", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: c.remoteAddr, Err: c.err.Load().(error)}
+ case buf := <-c.recvQueue:
+ return copy(p, buf), c.remoteAddr, nil
+ }
+}
+
+// WriteTo writes a packet to the currently active net.PacketConn. The addr
+// argument is ignored and instead replaced with the RedialPacketConn's own
+// remote address.
+func (c *RedialPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
+ // addr is ignored.
+ select {
+ case <-c.closed:
+ return 0, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Source: c.LocalAddr(), Addr: c.remoteAddr, Err: c.err.Load().(error)}
+ default:
+ }
+ buf := make([]byte, len(p))
+ copy(buf, p)
+ select {
+ case c.sendQueue <- buf:
+ return len(buf), nil
+ default:
+ // Drop the outgoing packet if the send queue is full.
+ return len(buf), nil
+ }
+}
+
+// closeWithError unblocks pending operations and makes future operations fail
+// with the given error. If err is nil, it becomes errClosedPacketConn.
+func (c *RedialPacketConn) closeWithError(err error) error {
+ var once bool
+ c.closeOnce.Do(func() {
+ // Store the error to be returned by future read/write
+ // operations.
+ if err == nil {
+ err = errors.New("operation on closed connection")
+ }
+ c.err.Store(err)
+ close(c.closed)
+ once = true
+ })
+ if !once {
+ return &net.OpError{Op: "close", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)}
+ }
+ return nil
+}
+
+// Close unblocks pending operations and makes future operations fail with a
+// "closed connection" error.
+func (c *RedialPacketConn) Close() error {
+ return c.closeWithError(nil)
+}
+
+// LocalAddr returns the localAddr value that was passed to NewRedialPacketConn.
+func (c *RedialPacketConn) LocalAddr() net.Addr { return c.localAddr }
+
+func (c *RedialPacketConn) SetDeadline(t time.Time) error { return errNotImplemented }
+func (c *RedialPacketConn) SetReadDeadline(t time.Time) error { return errNotImplemented }
+func (c *RedialPacketConn) SetWriteDeadline(t time.Time) error { return errNotImplemented }
diff --git a/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/util/util.go b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/util/util.go
new file mode 100644
index 0000000..00f7302
--- /dev/null
+++ b/vendor/git.torproject.org/pluggable-transports/snowflake.git/common/util/util.go
@@ -0,0 +1,99 @@
+package util
+
+import (
+ "encoding/json"
+ "errors"
+ "net"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/sdp/v3"
+ "github.com/pion/webrtc/v3"
+)
+
+func SerializeSessionDescription(desc *webrtc.SessionDescription) (string, error) {
+ bytes, err := json.Marshal(*desc)
+ if err != nil {
+ return "", err
+ }
+ return string(bytes), nil
+}
+
+func DeserializeSessionDescription(msg string) (*webrtc.SessionDescription, error) {
+ var parsed map[string]interface{}
+ err := json.Unmarshal([]byte(msg), &parsed)
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := parsed["type"]; !ok {
+ return nil, errors.New("cannot deserialize SessionDescription without type field")
+ }
+ if _, ok := parsed["sdp"]; !ok {
+ return nil, errors.New("cannot deserialize SessionDescription without sdp field")
+ }
+
+ var stype webrtc.SDPType
+ switch parsed["type"].(string) {
+ default:
+ return nil, errors.New("Unknown SDP type")
+ case "offer":
+ stype = webrtc.SDPTypeOffer
+ case "pranswer":
+ stype = webrtc.SDPTypePranswer
+ case "answer":
+ stype = webrtc.SDPTypeAnswer
+ case "rollback":
+ stype = webrtc.SDPTypeRollback
+ }
+
+ return &webrtc.SessionDescription{
+ Type: stype,
+ SDP: parsed["sdp"].(string),
+ }, nil
+}
+
+// Stolen from https://github.com/golang/go/pull/30278
+func IsLocal(ip net.IP) bool {
+ if ip4 := ip.To4(); ip4 != nil {
+ // Local IPv4 addresses are defined in https://tools.ietf.org/html/rfc1918
+ return ip4[0] == 10 ||
+ (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
+ (ip4[0] == 192 && ip4[1] == 168) ||
+ // Carrier-Grade NAT as per https://tools.ietf.org/htm/rfc6598
+ (ip4[0] == 100 && ip4[1]&0xc0 == 64) ||
+ // Dynamic Configuration as per https://tools.ietf.org/htm/rfc3927
+ (ip4[0] == 169 && ip4[1] == 254)
+ }
+ // Local IPv6 addresses are defined in https://tools.ietf.org/html/rfc4193
+ return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
+}
+
+// Removes local LAN address ICE candidates
+func StripLocalAddresses(str string) string {
+ var desc sdp.SessionDescription
+ err := desc.Unmarshal([]byte(str))
+ if err != nil {
+ return str
+ }
+ for _, m := range desc.MediaDescriptions {
+ attrs := make([]sdp.Attribute, 0)
+ for _, a := range m.Attributes {
+ if a.IsICECandidate() {
+ c, err := ice.UnmarshalCandidate(a.Value)
+ if err == nil && c.Type() == ice.CandidateTypeHost {
+ ip := net.ParseIP(c.Address())
+ if ip != nil && (IsLocal(ip) || ip.IsUnspecified() || ip.IsLoopback()) {
+ /* no append in this case */
+ continue
+ }
+ }
+ }
+ attrs = append(attrs, a)
+ }
+ m.Attributes = attrs
+ }
+ bts, err := desc.Marshal()
+ if err != nil {
+ return str
+ }
+ return string(bts)
+}