commit d433318f1635b9d14b29236df7556cb42378932b
Author: David Fifield <[email protected]>
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
 }
 

_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to