[tor-commits] [goptlib/master] Add support for TOR_PT_PROXY.
dcf at torproject.org
dcf at torproject.org
Sun Jun 28 03:00:59 UTC 2015
commit d433318f1635b9d14b29236df7556cb42378932b
Author: David Fifield <david at bamsoftware.com>
Date: Wed Jun 24 13:25:06 2015 -0700
Add support for TOR_PT_PROXY.
https://trac.torproject.org/projects/tor/ticket/12125
Adds two new functions:
ProxyError
ProxyDone
and one new member to ClientInfo:
ProxyURL *url.URL
---
examples/dummy-client/dummy-client.go | 5 +++
proxy_test.go | 80 +++++++++++++++++++++++++++++++++
pt.go | 66 ++++++++++++++++++++++++++-
3 files changed, 150 insertions(+), 1 deletion(-)
diff --git a/examples/dummy-client/dummy-client.go b/examples/dummy-client/dummy-client.go
index cbb2d38..5d99b0c 100644
--- a/examples/dummy-client/dummy-client.go
+++ b/examples/dummy-client/dummy-client.go
@@ -88,6 +88,11 @@ func main() {
os.Exit(1)
}
+ if ptInfo.ProxyURL != nil {
+ pt.ProxyError("proxy is not supported")
+ os.Exit(1)
+ }
+
listeners := make([]net.Listener, 0)
for _, methodName := range ptInfo.MethodNames {
switch methodName {
diff --git a/proxy_test.go b/proxy_test.go
new file mode 100644
index 0000000..c7a113b
--- /dev/null
+++ b/proxy_test.go
@@ -0,0 +1,80 @@
+package pt
+
+import (
+ "os"
+ "testing"
+)
+
+func TestGetProxyURL(t *testing.T) {
+ badTests := [...]string{
+ "bogus",
+ "http:",
+ "://127.0.0.1",
+ "//127.0.0.1",
+ "http:127.0.0.1",
+ "://[::1]",
+ "//[::1]",
+ "http:[::1]",
+ "://localhost",
+ "//localhost",
+ "http:localhost",
+ // No port in these.
+ "http://127.0.0.1",
+ "socks4a://127.0.0.1",
+ "socks5://127.0.0.1",
+ "http://127.0.0.1:",
+ "http://[::1]",
+ "http://localhost",
+ "unknown://localhost/whatever",
+ // No host in these.
+ "http://:8080",
+ "socks4a://:1080",
+ "socks5://:1080",
+ }
+ goodTests := [...]struct {
+ input, expected string
+ }{
+ {"http://127.0.0.1:8080", "http://127.0.0.1:8080"},
+ {"http://127.0.0.1:8080/", "http://127.0.0.1:8080/"},
+ {"http://127.0.0.1:8080/path", "http://127.0.0.1:8080/path"},
+ {"http://[::1]:8080", "http://[::1]:8080"},
+ {"http://[::1]:8080/", "http://[::1]:8080/"},
+ {"http://[::1]:8080/path", "http://[::1]:8080/path"},
+ {"http://localhost:8080", "http://localhost:8080"},
+ {"http://localhost:8080/", "http://localhost:8080/"},
+ {"http://localhost:8080/path", "http://localhost:8080/path"},
+ {"http://user@localhost:8080", "http://user@localhost:8080"},
+ {"http://user:password@localhost:8080", "http://user:password@localhost:8080"},
+ {"socks5://localhost:1080", "socks5://localhost:1080"},
+ {"socks4a://localhost:1080", "socks4a://localhost:1080"},
+ {"unknown://localhost:9999/whatever", "unknown://localhost:9999/whatever"},
+ }
+
+ os.Clearenv()
+ u, err := getProxyURL()
+ if err != nil {
+ t.Errorf("empty environment unexpectedly returned an error: %s", err)
+ }
+ if u != nil {
+ t.Errorf("empty environment returned %q", u)
+ }
+
+ for _, input := range badTests {
+ os.Setenv("TOR_PT_PROXY", input)
+ u, err = getProxyURL()
+ if err == nil {
+ t.Errorf("TOR_PT_PROXY=%q unexpectedly succeeded and returned %q", input, u)
+ }
+ }
+
+ for _, test := range goodTests {
+ os.Setenv("TOR_PT_PROXY", test.input)
+ u, err := getProxyURL()
+ if err != nil {
+ t.Errorf("TOR_PT_PROXY=%q unexpectedly returned an error: %s", test.input, err)
+ }
+ if u == nil || u.String() != test.expected {
+ t.Errorf("TOR_PT_PROXY=%q â %q (expected %q)", test.input, u, test.expected)
+ }
+ }
+}
diff --git a/pt.go b/pt.go
index d2e7dc1..fc2141e 100644
--- a/pt.go
+++ b/pt.go
@@ -39,6 +39,12 @@
// 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("proxy %s is not supported")
+// os.Exit(1)
+// }
// for _, methodName := range ptInfo.MethodNames {
// switch methodName {
// case "foo":
@@ -119,6 +125,9 @@
// 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 SOCKS4a server sufficient for a Tor client transport
// plugin.
//
@@ -135,6 +144,7 @@ import (
"fmt"
"io"
"net"
+ "net/url"
"os"
"strconv"
"strings"
@@ -286,6 +296,12 @@ 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 %s\n", 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) {
@@ -326,6 +342,11 @@ func SmethodsDone() {
line("SMETHODS", "DONE")
}
+// Emit a PROXY DONE line. Call this after parsing ClientInfo.ProxyURL.
+func ProxyDone() {
+ fmt.Fprintf(Stdout, "PROXY DONE\n")
+}
+
// 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.
@@ -370,10 +391,48 @@ func getClientTransports(star []string) ([]string, error) {
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.
+// 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
@@ -401,6 +460,11 @@ func ClientSetup(star []string) (info ClientInfo, err error) {
return
}
+ info.ProxyURL, err = getProxyURL()
+ if err != nil {
+ return
+ }
+
return info, nil
}
More information about the tor-commits
mailing list