From ae6d97af379904443db3a039375d71817b4cc8a9 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sat, 10 Oct 2015 14:14:31 +0000 Subject: [PATCH] Add c.Dialer(auth) which returns a net.proxy. A bit more involved than David S' version, since it queries the control port for an appropriate SOCKS listener. This makes it easier to use. --- dialer.go | 54 ++++++++++++++++++++++++++++++++++++++ examples/dialer/dialer.go | 55 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 dialer.go create mode 100644 examples/dialer/dialer.go diff --git a/dialer.go b/dialer.go new file mode 100644 index 0000000..3b3c0cb --- /dev/null +++ b/dialer.go @@ -0,0 +1,54 @@ +// dialer.go - Tor backed proxy.Dialer. +// +// To the extent possible under law, Yawning Angel waived all copyright +// and related or neighboring rights to bulb, using the creative +// commons "cc0" public domain dedication. See LICENSE or +// for full details. + +package bulb + +import ( + "strconv" + "strings" + + "golang.org/x/net/proxy" +) + +// Dialer returns a proxy.Dialer for the given Tor instance. +func (c *Conn) Dialer(auth *proxy.Auth) (proxy.Dialer, error) { + const ( + cmdGetInfo = "GETINFO" + socksListeners = "net/listeners/socks" + unixPrefix = "unix:" + ) + + // Query for the SOCKS listeners via a GETINFO request. + resp, err := c.Request("%s %s", cmdGetInfo, socksListeners) + if err != nil { + return nil, err + } + + if len(resp.Data) != 1 { + return nil, newProtocolError("no SOCKS listeners configured") + } + splitResp := strings.Split(resp.Data[0], " ") + if len(splitResp) < 1 { + return nil, newProtocolError("no SOCKS listeners configured") + } + + // The first listener will have a "net/listeners/socks=" prefix, and all + // entries are QuotedStrings. + laddrStr := strings.TrimPrefix(splitResp[0], socksListeners+"=") + if laddrStr == splitResp[0] { + return nil, newProtocolError("failed to parse SOCKS listener") + } + laddrStr, _ = strconv.Unquote(laddrStr) + + // Construct the proxyDialer. + if strings.HasPrefix(laddrStr, unixPrefix) { + unixPath := strings.TrimPrefix(laddrStr, unixPrefix) + return proxy.SOCKS5("unix", unixPath, auth, proxy.Direct) + } + + return proxy.SOCKS5("tcp", laddrStr, auth, proxy.Direct) +} diff --git a/examples/dialer/dialer.go b/examples/dialer/dialer.go new file mode 100644 index 0000000..f08a636 --- /dev/null +++ b/examples/dialer/dialer.go @@ -0,0 +1,55 @@ +// Dialer example. +// +// To the extent possible under law, Yawning Angel waived all copyright +// and related or neighboring rights to bulb, using the creative +// commons "cc0" public domain dedication. See LICENSE or +// for full details. + +package main + +import ( + "io/ioutil" + "log" + "net/http" + + "github.com/yawning/bulb" +) + +func main() { + // Connect to a running tor instance. + // * TCP: c, err := bulb.Dial("tcp4", "127.0.0.1:9051") + c, err := bulb.Dial("unix", "/var/run/tor/control") + if err != nil { + log.Fatalf("failed to connect to control port: %v", err) + } + defer c.Close() + + // See what's really going on under the hood. + // Do not enable in production. + c.Debug(true) + + // Authenticate with the control port. The password argument + // here can be "" if no password is set (CookieAuth, no auth). + if err := c.Authenticate("ExamplePassword"); err != nil { + log.Fatalf("Authentication failed: %v", err) + } + + // Get a proxy.Dialer that will use the given Tor instance for outgoing + // connections. + dialer, err := c.Dialer(nil) + if err != nil { + log.Fatalf("Failed to get Dialer: %v", err) + } + + // Try using the Dialer for something... + orTransport := &http.Transport{Dial: dialer.Dial} + orHTTPClient := &http.Client{Transport: orTransport} + resp, err := orHTTPClient.Get("https://check.torproject.org/api/ip") + if err != nil { + log.Fatalf("Failed https GET: %v", err) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + log.Printf("HTTPS GET via Tor: %v", resp) + log.Printf(" Body: %s\n", body) +}