From 49e3152a70145f9361c60ee919a8d71d3b2979fc Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 17 Dec 2020 06:43:37 +0100 Subject: [PATCH] WIP Signed-off-by: Jorropo --- README.md | 25 ++++++++----- builder.go | 69 ++++++++++++++++++++++++++++++++++++ config/config.go | 38 +++++++++++--------- config/embeded_disabled.go | 6 ++-- config/embeded_enabled.go | 14 ++++---- conn.go | 5 ++- dns-helpers/dot.go | 46 ++++++++++++++++++++++++ go.mod | 5 +++ go.sum | 4 +++ internal/confStore/config.go | 6 ++-- transport.go | 44 +++++------------------ 11 files changed, 188 insertions(+), 74 deletions(-) create mode 100644 builder.go create mode 100644 dns-helpers/dot.go diff --git a/README.md b/README.md index 683d4cb..98eec7c 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,28 @@ # go-libp2p-tor-transport Go tor transport is a [go-libp2p](https://github.com/libp2p/go-libp2p) transport targeting mainly \*nix platform. -## WIP -This transport is in very early stages (PoC) many of features enumerated here are just targets. - You can follow the process of the **1.0** [here](https://github.com/berty/go-libp2p-tor-transport/projects/1). ## Usage : ```go +package main import ( "context" tor "berty.tech/go-libp2p-tor-transport" + dns "berty.tech/go-libp2p-tor-transport/dns-helpers" config "berty.tech/go-libp2p-tor-transport/config" libp2p "github.com/libp2p/go-libp2p" ) func main() { builder, err := tor.NewBuilder( // Create a builder - config.EnableEmbeded, // Use the embeded tor instance. + config.EnableEmbeded(), // Use the embeded tor instance. ) c(err) host, err := libp2p.New( // Create a libp2p node context.Background(), - libp2p.Transport(builder), // Use the builder to create a transport instance (you can't reuse the same builder after that). + libp2p.Transport(builder.GetTransportConstructor()), // Use the builder to create a transport instance (you can't reuse the same builder after that). ) c(err) } @@ -37,6 +36,7 @@ func c(err error) { // Used to check error in this example, replace by whatever ### With config : ```go +package main import ( "context" "time" @@ -44,19 +44,28 @@ import ( tor "berty.tech/go-libp2p-tor-transport" config "berty.tech/go-libp2p-tor-transport/config" libp2p "github.com/libp2p/go-libp2p" + madns "github.com/multiformats/go-multiaddr-dns" ) func main() { builder, err := tor.NewBuilder( // NewBuilder can accept some `config.Configurator` - config.AllowTcpDial, // Some Configurator are already ready to use. + config.AllowTcpDial(), // Some Configurator are already ready to use. config.SetSetupTimeout(time.Minute), // Some require a parameter, in this case it's a function that will return a Configurator. config.SetBinaryPath("/usr/bin/tor"), ) - // Evrything else is as previously shown. c(err) + // Sets the default madns resolver, if you don't do that dns requests will be done clearly over internet. + r, err := dns.CreateDoTMaDNSResolverFromDialContext( + builder.GetDialer().DialContext, // Dialer + "cloudflare-dns.com", // Hostname + "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001", // Addresses + ) + c(err) + madns.DefaultResolver = r + // Everything else is as previously shown. hostWithConfig, err := libp2p.New( context.Background(), - libp2p.Transport(builder), + libp2p.Transport(builder.GetTransportConstructor()), ) c(err) } diff --git a/builder.go b/builder.go new file mode 100644 index 0000000..6164613 --- /dev/null +++ b/builder.go @@ -0,0 +1,69 @@ +package tor + +import ( + "context" + "net" + "time" + + "github.com/cretz/bine/tor" + + "github.com/joomcode/errorx" + "golang.org/x/net/proxy" + + "berty.tech/go-libp2p-tor-transport/config" + "berty.tech/go-libp2p-tor-transport/internal/confStore" +) + +// Builder is the type holding the starter node, it's used to fetch different ressources. +type Builder struct { + allowTcpDial bool + setupTimeout time.Duration + bridge *tor.Tor + dialer ContextDialer +} + +// ContextDialer is a dialler that also support contexted dials. +type ContextDialer interface { + proxy.Dialer + DialContext(ctx context.Context, network string, addr string) (net.Conn, error) +} + +func NewBuilder(cs ...config.Configurator) (*Builder, error) { + var conf confStore.Config + { + // Applying configuration + conf = confStore.Config{ + SetupTimeout: 5 * time.Minute, + RunningContext: context.Background(), + TorStart: &tor.StartConf{ + EnableNetwork: true, // Do Fast Start + }, + } + if err := config.Merge(cs...)(&conf); err != nil { + return nil, errorx.Decorate(err, "Can't apply configuration to the tor node") + } + } + t, err := tor.Start(conf.RunningContext, conf.TorStart) + if err != nil { + return nil, errorx.Decorate(err, "Can't start tor node") + } + + // Up until this point, we don't need the starting configuration anymore. + conf.TorStart = nil + + dialer, err := t.Dialer(conf.RunningContext, nil) + if err != nil { + return nil, errorx.Decorate(err, "Can't create a dialer.") + } + return &Builder{ + allowTcpDial: conf.AllowTcpDial, + setupTimeout: conf.SetupTimeout, + bridge: t, + dialer: dialer, + }, nil +} + +// GetDialer returns a shared dialer, it is closed once the transport closes. +func (b *Builder) GetDialer() ContextDialer { + return b.dialer +} diff --git a/config/config.go b/config/config.go index 9879742..f7a91f7 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "context" "io" "time" @@ -10,13 +11,6 @@ import ( "berty.tech/go-libp2p-tor-transport/internal/confStore" ) -// Check that all configurator are correctly done : -var _ = []Configurator{ - AllowTcpDial, - DoSlowStart, - EnableEmbeded, -} - type Configurator func(*confStore.Config) error // ConfMerge Merges different configs, starting at the first ending at the last. @@ -33,16 +27,20 @@ func Merge(cs ...Configurator) Configurator { // AllowTcpDial allows the tor transport to dial tcp address. // By Default TcpDial is off. -func AllowTcpDial(c *confStore.Config) error { - c.AllowTcpDial = true - return nil +func AllowTcpDial() Configurator { + return func(c *confStore.Config) error { + c.AllowTcpDial = true + return nil + } } // DoSlowStart set the tor node to bootstrap only when a Dial or a Listen is issued. // By Default DoSlowStart is off. -func DoSlowStart(c *confStore.Config) error { - c.TorStart.EnableNetwork = false - return nil +func DoSlowStart() Configurator { + return func(c *confStore.Config) error { + c.TorStart.EnableNetwork = false + return nil + } } // SetSetupTimeout change the timeout for the bootstrap of the node and the publication of the tunnel. @@ -79,13 +77,11 @@ func SetBinaryPath(path string) Configurator { // SetTemporaryDirectory sets the temporary directory where Tor is gonna put his // data dir. -// If you want an easy way to find it you can use: -// https://github.com/Jorropo/go-temp-dir func SetTemporaryDirectory(path string) Configurator { rpath, err := realpath.Realpath(path) return func(c *confStore.Config) error { if err != nil { - errorx.Decorate(err, "Can't resolve path") + return errorx.Decorate(err, "Can't resolve path") } c.TorStart.TempDataDirBase = rpath return nil @@ -97,9 +93,17 @@ func SetTorrcPath(path string) Configurator { rpath, err := realpath.Realpath(path) return func(c *confStore.Config) error { if err != nil { - errorx.Decorate(err, "Can't resolve path") + return errorx.Decorate(err, "Can't resolve path") } c.TorStart.TorrcFile = rpath return nil } } + +// SetRunningContext sets the context used for the running of the node. +func SetRunningContext(ctx context.Context) Configurator { + return func(c *confStore.Config) error { + c.RunningContext = ctx + return nil + } +} diff --git a/config/embeded_disabled.go b/config/embeded_disabled.go index 8c74975..8b090e3 100644 --- a/config/embeded_disabled.go +++ b/config/embeded_disabled.go @@ -11,6 +11,8 @@ import ( // EnableEmbeded setups the node to use a builtin tor node. // Note: you will need to build with `-tags=embedTor` for this to works. // Not available on all systems. -func EnableEmbeded(c *confStore.Config) error { - return fmt.Errorf("You havn't enabled the embeded tor instance at compilation. You can enable it with `-tags=embedTor` while building.") +func EnableEmbeded() Configurator { + return func(c *confStore.Config) error { + return fmt.Errorf("You havn't enabled the embeded tor instance at compilation. You can enable it with `-tags=embedTor` while building.") + } } diff --git a/config/embeded_enabled.go b/config/embeded_enabled.go index 163dcb6..d7c34de 100644 --- a/config/embeded_enabled.go +++ b/config/embeded_enabled.go @@ -13,11 +13,13 @@ import ( // EnableEmbeded setups the node to use a builtin tor node. // Note: you will need to build with `-tags=embedTor` for this to works. // Not available on all systems. -func EnableEmbeded(c *confStore.Config) error { - if libtor.Available { - c.TorStart.ProcessCreator = libtor.Creator - c.TorStart.UseEmbeddedControlConn = true - return nil +func EnableEmbeded() Configurator { + return func(c *confStore.Config) error { + if libtor.Available { + c.TorStart.ProcessCreator = libtor.Creator + c.TorStart.UseEmbeddedControlConn = true + return nil + } + return fmt.Errorf("The embeded Tor node isn't available. Check your arch and CGO.") } - return fmt.Errorf("The embeded Tor node isn't available. Check your arch and CGO.") } diff --git a/conn.go b/conn.go index bab4c84..3676d5d 100644 --- a/conn.go +++ b/conn.go @@ -1,6 +1,7 @@ package tor import ( + "io" "net" "time" @@ -99,9 +100,7 @@ func (c *dialConnTcp) LocalMultiaddr() ma.Multiaddr { // netConnWithoutAddr is a net.Conn like but without LocalAddr and RemoteAddr. type netConnWithoutAddr interface { - Read(b []byte) (n int, err error) - Write(b []byte) (n int, err error) - Close() error + io.ReadWriteCloser SetDeadline(t time.Time) error SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error diff --git a/dns-helpers/dot.go b/dns-helpers/dot.go new file mode 100644 index 0000000..b96b024 --- /dev/null +++ b/dns-helpers/dot.go @@ -0,0 +1,46 @@ +package dns + +import ( + "crypto/tls" + "crypto/x509" + "net" + "time" + + "github.com/joomcode/errorx" + madns "github.com/multiformats/go-multiaddr-dns" + "github.com/ncruces/go-dns" +) + +func CreatDoTDNSResolverFromDialContext(dialFunc dns.DialFunc, hostname string, addresses ...string) (*net.Resolver, error) { + certPool, err := x509.SystemCertPool() + if err != nil { + return nil, errorx.Decorate(err, "can't fetch system cert pool") + } + resolver, err := dns.NewDoTResolver( + hostname, + dns.DoTAddresses(addresses...), + dns.DoTCache( + dns.MaxCacheEntries(256), + dns.MaxCacheTTL(time.Hour*24), + dns.MinCacheTTL(time.Minute), + ), + dns.DoTConfig(&tls.Config{ + RootCAs: certPool, + }), + dns.DoTDialFunc(dialFunc), + ) + if err != nil { + return nil, errorx.Decorate(err, "can't create DoT resolver") + } + return resolver, nil +} + +func CreateDoTMaDNSResolverFromDialContext(dialFunc dns.DialFunc, hostname string, addresses ...string) (*madns.Resolver, error) { + netResolver, err := CreatDoTDNSResolverFromDialContext(dialFunc, hostname, addresses...) + if err != nil { + return nil, err + } + return &madns.Resolver{ + Backend: netResolver, + }, nil +} diff --git a/go.mod b/go.mod index a2f694d..15bd896 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,12 @@ require ( github.com/libp2p/go-libp2p-core v0.6.1 github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 github.com/multiformats/go-multiaddr v0.3.1 + github.com/multiformats/go-multiaddr-dns v0.2.1-0.20201130213411-dba25a2c0b7a github.com/multiformats/go-multiaddr-fmt v0.1.0 + github.com/ncruces/go-dns v1.0.0 github.com/yookoala/realpath v1.0.0 + golang.org/x/net v0.0.0-20200822124328-c89045814202 golang.org/x/tools v0.0.0-20200904185747-39188db58858 // indirect ) + +replace github.com/ncruces/go-dns => github.com/berty/go-dns v1.0.1 // temporary, see https://github.com/ncruces/go-dns/pull/8/ diff --git a/go.sum b/go.sum index 3143280..fc32a62 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/berty/go-dns v1.0.1 h1:5fyswL5QqJ/78hLUc5Go6e0VA0u7Bww/vyoddZpQyM4= +github.com/berty/go-dns v1.0.1/go.mod h1:su21ON0Nu6vkydXbOgso3ZN05o2jguVv+utey3mKP8A= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= @@ -132,6 +134,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I= github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= +github.com/multiformats/go-multiaddr-dns v0.2.1-0.20201130213411-dba25a2c0b7a h1:Pw459lu1YP0+F78E5fiX+g/DWEyTo+EaTmgrPwU/vW8= +github.com/multiformats/go-multiaddr-dns v0.2.1-0.20201130213411-dba25a2c0b7a/go.mod h1:mNzQ4eTGDg0ll1N9jKPOUogZPoJ30W8a7zk66FQPpdQ= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.1.5 h1:QoRKvu0xHN1FCFJcMQLbG/yQE2z441L5urvG3+qyz7g= diff --git a/internal/confStore/config.go b/internal/confStore/config.go index c39e442..1fc1e6a 100644 --- a/internal/confStore/config.go +++ b/internal/confStore/config.go @@ -1,6 +1,7 @@ package confStore import ( + "context" "time" "github.com/cretz/bine/tor" @@ -8,8 +9,9 @@ import ( // `Config` stores the config, don't use it, you must use Configurator. type Config struct { - AllowTcpDial bool - SetupTimeout time.Duration + AllowTcpDial bool + SetupTimeout time.Duration + RunningContext context.Context TorStart *tor.StartConf } diff --git a/transport.go b/transport.go index f31ec39..181b779 100644 --- a/transport.go +++ b/transport.go @@ -8,9 +8,6 @@ import ( "sync" "time" - "berty.tech/go-libp2p-tor-transport/config" - "berty.tech/go-libp2p-tor-transport/internal/confStore" - "github.com/cretz/bine/tor" "github.com/libp2p/go-libp2p-core/peer" @@ -27,7 +24,7 @@ import ( type transport struct { // bridge is the main tor object, he focuses interactions. bridge *tor.Tor - dialer *tor.Dialer + dialer ContextDialer // Used to upgrade unsecure TCP connections to secure multiplexed and // authenticate Tor connections. @@ -56,42 +53,17 @@ type listenHolder struct { next *listenHolder } -func NewBuilder(cs ...config.Configurator) (func(*tptu.Upgrader) tpt.Transport, error) { - var conf confStore.Config - { - // Applying configuration - c := &confStore.Config{ - SetupTimeout: 5 * time.Minute, - TorStart: &tor.StartConf{ - EnableNetwork: true, // Do Fast Start - }, - } - if err := config.Merge(cs...)(c); err != nil { - return nil, errorx.Decorate(err, "Can't apply configuration to the tor node") - } - conf = *c - } - t, err := tor.Start(context.Background(), conf.TorStart) - if err != nil { - return nil, errorx.Decorate(err, "Can't start tor node") - } - - // Up until this point, we don't need the starting configuration anymore. - conf.TorStart = nil - - dialer, err := t.Dialer(context.Background(), nil) - if err != nil { - return nil, errorx.Decorate(err, "Can't create a dialer.") - } +// GetTransportConstructor create a libp2p argument to pass to libp2p.Transport. +func (b *Builder) GetTransportConstructor() func(*tptu.Upgrader) tpt.Transport { return func(u *tptu.Upgrader) tpt.Transport { return &transport{ - allowTcpDial: conf.AllowTcpDial, - setupTimeout: conf.SetupTimeout, - bridge: t, - dialer: dialer, + allowTcpDial: b.allowTcpDial, + setupTimeout: b.setupTimeout, + bridge: b.bridge, + dialer: b.dialer, upgrader: u, } - }, nil + } } func (_ *transport) Proxy() bool {