TCP connection over Tor in Golang
A tor node acts as a SOCKS proxy on port 9050. Go support for the SOCKS5 protocol lives in package golang.org/x/net/proxy
:
import "golang.org/x/net/proxy"
In order to make connections through tor, you first need to create a new Dialer
that goes through the local SOCKS5 proxy:
dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, nil)
if err != nil {
log.Fatal(err)
}
To use this dialer, you just call dialer.Dial
instead of net.Dial
:
conn, err := dialer.Dial("tcp", "stackoverflow.com:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
the most portable way to achieve this is to use the wrapper wrote by Chad Retz
It will embed tor required assets into your binary. So that you can distribute it and ensure your app goes through tor.
In that scenario the end user does not need to install TBB or anything else other than your app.
Though to be honest, it does not include automatic updates of the tor engine, so this is something to perform on your end to ensure top security.
package main
import (
"context"
"fmt"
"log"
"net/http"
"time"
"github.com/cretz/bine/tor"
)
func main() {
// Start tor with default config (can set start conf's DebugWriter to os.Stdout for debug logs)
fmt.Println("Starting and registering onion service, please wait a couple of minutes...")
t, err := tor.Start(nil, nil)
if err != nil {
log.Panicf("Unable to start Tor: %v", err)
}
defer t.Close()
// Wait at most a few minutes to publish the service
listenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer listenCancel()
// Create a v3 onion service to listen on any port but show as 80
onion, err := t.Listen(listenCtx, &tor.ListenConf{Version3: true, RemotePorts: []int{80}})
if err != nil {
log.Panicf("Unable to create onion service: %v", err)
}
defer onion.Close()
fmt.Printf("Open Tor browser and navigate to http://%v.onion\n", onion.ID)
fmt.Println("Press enter to exit")
// Serve the current folder from HTTP
errCh := make(chan error, 1)
go func() { errCh <- http.Serve(onion, http.FileServer(http.Dir("."))) }()
// End when enter is pressed
go func() {
fmt.Scanln()
errCh <- nil
}()
if err = <-errCh; err != nil {
log.Panicf("Failed serving: %v", err)
}
}