nonterminal Notes.     about     archive     gemini     feed

torsocks can't be used with Go

Torsocks(1) is a nice little tool that can be used to use the Tor network with programs that were not written to use Tor. But it can’t be used with programs written in Go. Because this problem came up here again and is (to my knowledge) not documented anywhere, a short note about why this is so.

torsocks works by preloading a small shared library using LD_PRELOAD that reimplements the standard POSIX networking functions like connect(2) and gethostbyname(3) to tunnel traffic through the Tor network. Because of the LD_PRELOAD mechanism programs then use these new implementations and thus use the Tor network. This works fine with lots of programs and we can verify this using the https://check.torproject.org page:

$ torsocks wget --quiet -O - https://check.torproject.org | grep Congratulations
   Congratulations. This browser is configured to use Tor.
   Congratulations. This browser is configured to use Tor.

Using Wireshark one can also verify that DNS resolution also uses the Tor network.

Go mostly doesn’t use shared libraries, so that the LD_PRELOAD mechanism doesn’t work, but more importantly also doesn’t use the usual C networking functions. The Go standard library implements almost all of its functionality directly using the operating system’s system calls, instead of calling the standard libc networking functions. The socket functions preloaded by torsocks simple are not used by Go’s standard library. We can check this using a small example program:

$ cat main.go
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	resp, err := http.Get("https://check.torproject.org")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
}

Verifying with https://check.torproject.org shows that the program doesn’t use the Tor network even if run under torsocks.

$ go build main.go
$ ./main  | grep Sorry
	Sorry. You are not using Tor.
	Sorry. You are not using Tor.
$ torsocks ./main | grep Sorry
	Sorry. You are not using Tor.
	Sorry. You are not using Tor.

Unfortunately this error doesn’t become visible if one doesn’t try to connect to an Onion Service or uses a system that is configured to disallow all non-Tor traffic, such as Tails or Whonix.

Fortunately, it is easy to use the Tor network using the “proxy” package in the “net” sub-repository, which implements a SOCKS proxy client to connect to a local Tor router. Another small example program

$ cat main_tor.go
package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"golang.org/x/net/proxy"
)

func main() {
	proxyURL, err := url.Parse("socks5://127.0.0.1:9050")
	dialer, err := proxy.FromURL(proxyURL, proxy.Direct)
	if err != nil {
		panic(err)
	}
	transport := &http.Transport{Dial: dialer.Dial}
	client := http.Client{Transport: transport}

	resp, err := client.Get("https://check.torproject.org")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
}

$ go build main_tor.go
$ ./main_tor | grep Congratulations
	Congratulations. This browser is configured to use Tor.
	Congratulations. This browser is configured to use Tor.

A last small detail: On some operating systems it is possible to use the systems usual implementation for DNS resolution, by setting an environment variable or using the corresponding build tags:

$ export GODEBUG=netdns=cgo

Go programs will then use functions implemented by torsocks like getaddrinfo(3) for name resolution, but still not for the creation of TCP connections.