[Pkg-privacy-commits] [golang-goptlib] 12/20: Imported Upstream version 0.4

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 10:04:07 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository golang-goptlib.

commit cada8f9564c27145b0e257de44572f241ec86cce
Author: Ximin Luo <infinity0 at pwned.gg>
Date:   Mon Mar 30 04:54:31 2015 +0200

    Imported Upstream version 0.4
---
 README       |   8 ++---
 args.go      |  19 +++++-----
 args_test.go |  22 ++++++------
 pt.go        | 112 +++++++++++++++++++++++++++++++++++------------------------
 pt_test.go   |  80 ++++++++++++++++++++++++++----------------
 socks.go     |   7 ++--
 6 files changed, 144 insertions(+), 104 deletions(-)

diff --git a/README b/README
index f3ce5b3..c4ca377 100644
--- a/README
+++ b/README
@@ -1,8 +1,8 @@
 goptlib is a library for writing Tor pluggable transports in Go.
 
-https://gitweb.torproject.org/torspec.git/blob/HEAD:/pt-spec.txt
-https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/196-transport-control-ports.txt
-https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/217-ext-orport-auth.txt
+https://gitweb.torproject.org/torspec.git/tree/pt-spec.txt
+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
 
 To download a copy of the library into $GOPATH:
 	go get git.torproject.org/pluggable-transports/goptlib.git
@@ -15,7 +15,7 @@ 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:
-http://godoc.org/git.torproject.org/pluggable-transports/goptlib.git
+https://godoc.org/git.torproject.org/pluggable-transports/goptlib.git
 
 Report bugs to the tor-dev at lists.torproject.org mailing list or to the
 bug tracker at https://trac.torproject.org/projects/tor.
diff --git a/args.go b/args.go
index 5e96589..7672a35 100644
--- a/args.go
+++ b/args.go
@@ -2,7 +2,6 @@ package pt
 
 import (
 	"bytes"
-	"errors"
 	"fmt"
 	"sort"
 	"strings"
@@ -34,7 +33,7 @@ func (args Args) Add(key, value string) {
 }
 
 // Return the index of the next unescaped byte in s that is in the term set, or
-// else the length of the string if not terminators appear. Additionally return
+// 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
@@ -48,7 +47,7 @@ func indexUnescaped(s string, term []byte) (int, string, error) {
 		if b == '\\' {
 			i++
 			if i >= len(s) {
-				return 0, "", errors.New(fmt.Sprintf("nothing following final escape in %q", s))
+				return 0, "", fmt.Errorf("nothing following final escape in %q", s)
 			}
 			b = s[i]
 		}
@@ -82,7 +81,7 @@ func parseClientParameters(s string) (args Args, err error) {
 		i += offset
 		// End of string or no equals sign?
 		if i >= len(s) || s[i] != '=' {
-			err = errors.New(fmt.Sprintf("no equals sign in %q", s[begin:i]))
+			err = fmt.Errorf("no equals sign in %q", s[begin:i])
 			return
 		}
 		// Skip the equals sign.
@@ -94,7 +93,7 @@ func parseClientParameters(s string) (args Args, err error) {
 		}
 		i += offset
 		if len(key) == 0 {
-			err = errors.New(fmt.Sprintf("empty key in %q", s[begin:i]))
+			err = fmt.Errorf("empty key in %q", s[begin:i])
 			return
 		}
 		args.Add(key, value)
@@ -132,7 +131,7 @@ func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
 		i += offset
 		// End of string or no colon?
 		if i >= len(s) || s[i] != ':' {
-			err = errors.New(fmt.Sprintf("no colon in %q", s[begin:i]))
+			err = fmt.Errorf("no colon in %q", s[begin:i])
 			return
 		}
 		// Skip the colon.
@@ -145,7 +144,7 @@ func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
 		i += offset
 		// End of string or no equals sign?
 		if i >= len(s) || s[i] != '=' {
-			err = errors.New(fmt.Sprintf("no equals sign in %q", s[begin:i]))
+			err = fmt.Errorf("no equals sign in %q", s[begin:i])
 			return
 		}
 		// Skip the equals sign.
@@ -157,11 +156,11 @@ func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
 		}
 		i += offset
 		if len(methodName) == 0 {
-			err = errors.New(fmt.Sprintf("empty method name in %q", s[begin:i]))
+			err = fmt.Errorf("empty method name in %q", s[begin:i])
 			return
 		}
 		if len(key) == 0 {
-			err = errors.New(fmt.Sprintf("empty key in %q", s[begin:i]))
+			err = fmt.Errorf("empty key in %q", s[begin:i])
 			return
 		}
 		if opts[methodName] == nil {
@@ -200,7 +199,7 @@ func encodeSmethodArgs(args Args) string {
 	}
 
 	keys := make([]string, 0, len(args))
-	for key, _ := range args {
+	for key := range args {
 		keys = append(keys, key)
 	}
 	sort.Strings(keys)
diff --git a/args_test.go b/args_test.go
index 8a77251..134ac31 100644
--- a/args_test.go
+++ b/args_test.go
@@ -255,29 +255,29 @@ func TestParseServerTransportOptions(t *testing.T) {
 		{
 			"t:k=v",
 			map[string]Args{
-				"t": Args{"k": []string{"v"}},
+				"t": {"k": []string{"v"}},
 			},
 		},
 		{
 			"t1:k=v1;t2:k=v2;t1:k=v3",
 			map[string]Args{
-				"t1": Args{"k": []string{"v1", "v3"}},
-				"t2": Args{"k": []string{"v2"}},
+				"t1": {"k": []string{"v1", "v3"}},
+				"t2": {"k": []string{"v2"}},
 			},
 		},
 		{
 			"t\\:1:k=v;t\\=2:k=v;t\\;3:k=v;t\\\\4:k=v",
 			map[string]Args{
-				"t:1":  Args{"k": []string{"v"}},
-				"t=2":  Args{"k": []string{"v"}},
-				"t;3":  Args{"k": []string{"v"}},
-				"t\\4": Args{"k": []string{"v"}},
+				"t:1":  {"k": []string{"v"}},
+				"t=2":  {"k": []string{"v"}},
+				"t;3":  {"k": []string{"v"}},
+				"t\\4": {"k": []string{"v"}},
 			},
 		},
 		{
 			"t:k\\:1=v;t:k\\=2=v;t:k\\;3=v;t:k\\\\4=v",
 			map[string]Args{
-				"t": Args{
+				"t": {
 					"k:1":  []string{"v"},
 					"k=2":  []string{"v"},
 					"k;3":  []string{"v"},
@@ -288,14 +288,14 @@ func TestParseServerTransportOptions(t *testing.T) {
 		{
 			"t:k=v\\:1;t:k=v\\=2;t:k=v\\;3;t:k=v\\\\4",
 			map[string]Args{
-				"t": Args{"k": []string{"v:1", "v=2", "v;3", "v\\4"}},
+				"t": {"k": []string{"v:1", "v=2", "v;3", "v\\4"}},
 			},
 		},
 		{
 			"trebuchet:secret=nou;trebuchet:cache=/tmp/cache;ballista:secret=yes",
 			map[string]Args{
-				"trebuchet": Args{"secret": []string{"nou"}, "cache": []string{"/tmp/cache"}},
-				"ballista":  Args{"secret": []string{"yes"}},
+				"trebuchet": {"secret": []string{"nou"}, "cache": []string{"/tmp/cache"}},
+				"ballista":  {"secret": []string{"yes"}},
 			},
 		},
 	}
diff --git a/pt.go b/pt.go
index 8630cae..d2e7dc1 100644
--- a/pt.go
+++ b/pt.go
@@ -111,13 +111,13 @@
 // the example programs dummy-client and dummy-server.
 //
 // Tor pluggable transports specification:
-// https://gitweb.torproject.org/torspec.git/blob/HEAD:/pt-spec.txt.
+// https://gitweb.torproject.org/torspec.git/tree/pt-spec.txt.
 //
 // Extended ORPort:
-// https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/196-transport-control-ports.txt.
+// https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt.
 //
 // Extended ORPort Authentication:
-// https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/217-ext-orport-auth.txt.
+// https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt.
 //
 // The package implements a SOCKS4a server sufficient for a Tor client transport
 // plugin.
@@ -132,7 +132,6 @@ import (
 	"crypto/sha256"
 	"crypto/subtle"
 	"encoding/binary"
-	"errors"
 	"fmt"
 	"io"
 	"net"
@@ -202,35 +201,57 @@ func getenvRequired(key string) (string, error) {
 	return value, nil
 }
 
-// Escape a string so it contains no byte values over 127 and doesn't contain
-// any of the characters '\x00' or '\n'.
-func escape(s string) string {
-	var buf bytes.Buffer
-	for _, b := range []byte(s) {
-		if b == '\n' {
-			buf.WriteString("\\n")
-		} else if b == '\\' {
-			buf.WriteString("\\\\")
-		} else if 0 < b && b < 128 {
-			buf.WriteByte(b)
-		} else {
-			fmt.Fprintf(&buf, "\\x%02x", b)
+// 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) {
+		if b >= '0' && b <= '9' {
+			continue
+		}
+		if b >= 'A' && b <= 'Z' {
+			continue
+		}
+		if b >= 'a' && b <= 'z' {
+			continue
 		}
+		if b == '-' || b == '_' {
+			continue
+		}
+		return false
 	}
-	return buf.String()
+	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 {
-		buf.WriteString(" " + escape(x))
+		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 an
-// unescaped keyword, followed by any number of escaped strings.
+// 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...))
 }
@@ -352,7 +373,7 @@ func getClientTransports(star []string) ([]string, error) {
 // This structure is returned by ClientSetup. It consists of a list of method
 // names.
 type ClientInfo struct {
-	MethodNames   []string
+	MethodNames []string
 }
 
 // Check the client pluggable transports environment, emitting an error message
@@ -513,14 +534,14 @@ func readAuthCookie(f io.Reader) ([]byte, error) {
 	// Check that the file ends here.
 	n, err = f.Read(make([]byte, 1))
 	if n != 0 {
-		return nil, errors.New(fmt.Sprintf("file is longer than 64 bytes"))
+		return nil, fmt.Errorf("file is longer than 64 bytes")
 	} else if err != io.EOF {
-		return nil, errors.New(fmt.Sprintf("did not find EOF at end of file"))
+		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, errors.New(fmt.Sprintf("missing auth cookie header"))
+		return nil, fmt.Errorf("missing auth cookie header")
 	}
 
 	return cookie, nil
@@ -545,7 +566,7 @@ type ServerInfo struct {
 	Bindaddrs      []Bindaddr
 	OrAddr         *net.TCPAddr
 	ExtendedOrAddr *net.TCPAddr
-	AuthCookie     []byte
+	AuthCookiePath string
 }
 
 // Check the server pluggable transports environment, emitting an error message
@@ -591,17 +612,10 @@ func ServerSetup(star []string) (info ServerInfo, err error) {
 			return
 		}
 	}
-	authCookieFilename := getenv("TOR_PT_AUTH_COOKIE_FILE")
-	if authCookieFilename != "" {
-		info.AuthCookie, err = readAuthCookieFile(authCookieFilename)
-		if err != nil {
-			err = envError(fmt.Sprintf("error reading TOR_PT_AUTH_COOKIE_FILE %q: %s", authCookieFilename, err.Error()))
-			return
-		}
-	}
+	info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE")
 
 	// Need either OrAddr or ExtendedOrAddr.
-	if info.OrAddr == nil && (info.ExtendedOrAddr == nil || info.AuthCookie == nil) {
+	if info.OrAddr == nil && (info.ExtendedOrAddr == nil || info.AuthCookiePath == "") {
 		err = envError("need TOR_PT_ORPORT or TOR_PT_EXTENDED_SERVER_PORT environment variable")
 		return
 	}
@@ -644,12 +658,12 @@ func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error {
 		authTypes[b] = true
 	}
 	if count >= 256 {
-		return errors.New(fmt.Sprintf("read 256 auth types without seeing \\x00"))
+		return fmt.Errorf("read 256 auth types without seeing \\x00")
 	}
 
 	// We support only type 1, SAFE_COOKIE.
 	if !authTypes[1] {
-		return errors.New(fmt.Sprintf("server didn't offer auth type 1"))
+		return fmt.Errorf("server didn't offer auth type 1")
 	}
 	_, err := s.Write([]byte{1})
 	if err != nil {
@@ -679,12 +693,20 @@ func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error {
 		return err
 	}
 
-	expectedServerHash := computeServerHash(info.AuthCookie, clientNonce, serverNonce)
+	// 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.
+	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 errors.New(fmt.Sprintf("mismatch in server hash"))
+		return fmt.Errorf("mismatch in server hash")
 	}
 
-	clientHash = computeClientHash(info.AuthCookie, clientNonce, serverNonce)
+	clientHash = computeClientHash(authCookie, clientNonce, serverNonce)
 	_, err = s.Write(clientHash)
 	if err != nil {
 		return err
@@ -696,7 +718,7 @@ func extOrPortAuthenticate(s io.ReadWriter, info *ServerInfo) error {
 		return err
 	}
 	if status[0] != 1 {
-		return errors.New(fmt.Sprintf("server rejected authentication"))
+		return fmt.Errorf("server rejected authentication")
 	}
 
 	return nil
@@ -714,7 +736,7 @@ const (
 func extOrPortSendCommand(s io.Writer, cmd uint16, body []byte) error {
 	var buf bytes.Buffer
 	if len(body) > 65535 {
-		return errors.New(fmt.Sprintf("body length %d exceeds maximum of 65535", len(body)))
+		return fmt.Errorf("body length %d exceeds maximum of 65535", len(body))
 	}
 	err := binary.Write(&buf, binary.BigEndian, cmd)
 	if err != nil {
@@ -807,9 +829,9 @@ func extOrPortSetup(s io.ReadWriter, addr, methodName string) error {
 		return err
 	}
 	if cmd == extOrCmdDeny {
-		return errors.New("server returned DENY after our USERADDR and DONE")
+		return fmt.Errorf("server returned DENY after our USERADDR and DONE")
 	} else if cmd != extOrCmdOkay {
-		return errors.New(fmt.Sprintf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd))
+		return fmt.Errorf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd)
 	}
 
 	return nil
@@ -824,7 +846,7 @@ func extOrPortSetup(s io.ReadWriter, addr, methodName string) error {
 // 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.AuthCookie == nil {
+	if info.ExtendedOrAddr == nil || info.AuthCookiePath == "" {
 		return net.DialTCP("tcp", nil, info.OrAddr)
 	}
 
diff --git a/pt_test.go b/pt_test.go
index d6a53e1..8cdf6da 100644
--- a/pt_test.go
+++ b/pt_test.go
@@ -13,40 +13,60 @@ import (
 	"testing"
 )
 
-func stringIsSafe(s string) bool {
-	for _, c := range []byte(s) {
-		if c == '\x00' || c == '\n' || c > 127 {
-			return false
+func TestKeywordIsSafe(t *testing.T) {
+	tests := [...]struct {
+		keyword  string
+		expected bool
+	}{
+		{"", true},
+		{"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", true},
+		{"CMETHOD", true},
+		{"CMETHOD:", false},
+		{"a b c", false},
+		{"CMETHOD\x7f", false},
+		{"CMETHOD\x80", false},
+		{"CMETHOD\x81", false},
+		{"CMETHOD\xff", false},
+		{"CMÉTHOD", false},
+	}
+
+	for _, input := range tests {
+		isSafe := keywordIsSafe(input.keyword)
+		if isSafe != input.expected {
+			t.Errorf("keywordIsSafe(%q) → %v (expected %v)",
+				input.keyword, isSafe, input.expected)
 		}
 	}
-	return true
 }
 
-func TestEscape(t *testing.T) {
-	tests := [...]string{
-		"",
-		"abc",
-		"a\nb",
-		"a\\b",
-		"ab\\",
-		"ab\\\n",
-		"ab\n\\",
+func TestArgIsSafe(t *testing.T) {
+	tests := [...]struct {
+		arg      string
+		expected bool
+	}{
+		{"", true},
+		{"abc", true},
+		{"127.0.0.1:8000", true},
+		{"étude", false},
+		{"a\nb", false},
+		{"a\\b", true},
+		{"ab\\", true},
+		{"ab\\\n", false},
+		{"ab\n\\", false},
+		{"abc\x7f", true},
+		{"abc\x80", false},
+		{"abc\x81", false},
+		{"abc\xff", false},
+		{"abc\xff", false},
+		{"var=GVsbG8\\=", true},
 	}
 
-	check := func(input string) {
-		output := escape(input)
-		if !stringIsSafe(output) {
-			t.Errorf("escape(%q) → %q", input, output)
-		}
-	}
 	for _, input := range tests {
-		check(input)
-	}
-	for b := 0; b < 256; b++ {
-		// check one-byte string with each byte value 0–255
-		check(string([]byte{byte(b)}))
-		// check UTF-8 encoding of each character 0–255
-		check(string(b))
+		isSafe := argIsSafe(input.arg)
+		if isSafe != input.expected {
+			t.Errorf("argIsSafe(%q) → %v (expected %v)",
+				input.arg, isSafe, input.expected)
+		}
 	}
 }
 
@@ -755,7 +775,7 @@ func TestMakeStateDir(t *testing.T) {
 	}
 	defer os.RemoveAll(tempDir)
 
-	goodTests := [...]string {
+	goodTests := [...]string{
 		// Already existing directory.
 		tempDir,
 
@@ -786,13 +806,13 @@ func TestMakeStateDir(t *testing.T) {
 	os.Setenv("TOR_PT_STATE_LOCATION", tempFile)
 	_, err = MakeStateDir()
 	if err == nil {
-		t.Errorf("MakeStateDir with a file unexpectedly succeded")
+		t.Errorf("MakeStateDir with a file unexpectedly succeeded")
 	}
 
 	// Directory name that cannot be created. (Subdir of a file)
 	os.Setenv("TOR_PT_STATE_LOCATION", path.Join(tempFile, "subDir"))
 	_, err = MakeStateDir()
 	if err == nil {
-		t.Errorf("MakeStateDir with a subdirectory of a file unexpectedly succeded")
+		t.Errorf("MakeStateDir with a subdirectory of a file unexpectedly succeeded")
 	}
 }
diff --git a/socks.go b/socks.go
index f34f78f..6ad6542 100644
--- a/socks.go
+++ b/socks.go
@@ -2,7 +2,6 @@ package pt
 
 import (
 	"bufio"
-	"errors"
 	"fmt"
 	"io"
 	"net"
@@ -166,11 +165,11 @@ func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) {
 		return
 	}
 	if h[0] != socksVersion {
-		err = errors.New(fmt.Sprintf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion))
+		err = fmt.Errorf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion)
 		return
 	}
 	if h[1] != socksCmdConnect {
-		err = errors.New(fmt.Sprintf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect))
+		err = fmt.Errorf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect)
 		return
 	}
 
@@ -202,7 +201,7 @@ func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) {
 	}
 
 	if r.Buffered() != 0 {
-		err = errors.New(fmt.Sprintf("%d bytes left after SOCKS header", r.Buffered()))
+		err = fmt.Errorf("%d bytes left after SOCKS header", r.Buffered())
 		return
 	}
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/golang-goptlib.git



More information about the Pkg-privacy-commits mailing list