From 3c4fee0492b870362b150a9cef4586c92dc6c535 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Thu, 19 Apr 2018 10:30:40 -0400 Subject: [PATCH] tor auto config --- src/yggdrasil/config/config.go | 29 ++++++++++ src/yggdrasil/config/i2p.go | 7 +++ src/yggdrasil/config/tor.go | 11 ++++ src/yggdrasil/core.go | 1 + src/yggdrasil/dial.go | 99 ++++++++++++++++++++++++++++++++++ src/yggdrasil/tcp.go | 62 ++++++++++----------- yggdrasil.go | 33 ++++-------- 7 files changed, 186 insertions(+), 56 deletions(-) create mode 100644 src/yggdrasil/config/config.go create mode 100644 src/yggdrasil/config/i2p.go create mode 100644 src/yggdrasil/config/tor.go create mode 100644 src/yggdrasil/dial.go diff --git a/src/yggdrasil/config/config.go b/src/yggdrasil/config/config.go new file mode 100644 index 0000000..0861f61 --- /dev/null +++ b/src/yggdrasil/config/config.go @@ -0,0 +1,29 @@ +package config + +/** +* This is a very crude wrapper around src/yggdrasil +* It can generate a new config (--genconf) +* It can read a config from stdin (--useconf) +* It can run with an automatic config (--autoconf) + */ + +type NodeConfig struct { + Listen string + AdminListen string + Peers []string + BoxPub string + BoxPriv string + SigPub string + SigPriv string + Multicast bool + LinkLocal string + IfName string + IfTAPMode bool + IfMTU int + Net NetConfig +} + +type NetConfig struct { + Tor TorConfig + I2P I2PConfig +} diff --git a/src/yggdrasil/config/i2p.go b/src/yggdrasil/config/i2p.go new file mode 100644 index 0000000..375d413 --- /dev/null +++ b/src/yggdrasil/config/i2p.go @@ -0,0 +1,7 @@ +package config + +type I2PConfig struct { + Keyfile string + Addr string + Enabled bool +} diff --git a/src/yggdrasil/config/tor.go b/src/yggdrasil/config/tor.go new file mode 100644 index 0000000..aceb885 --- /dev/null +++ b/src/yggdrasil/config/tor.go @@ -0,0 +1,11 @@ +package config + +/** +*tor specific configuration + */ +type TorConfig struct { + OnionKeyfile string + SocksAddr string + UseForAll bool + Enabled bool +} diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index be0c6ae..d68a601 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -19,6 +19,7 @@ type Core struct { tun tunDevice admin admin searches searches + Dialer Dialer tcp *tcpInterface udp *udpInterface log *log.Logger diff --git a/src/yggdrasil/dial.go b/src/yggdrasil/dial.go new file mode 100644 index 0000000..d9249a6 --- /dev/null +++ b/src/yggdrasil/dial.go @@ -0,0 +1,99 @@ +package yggdrasil + +import ( + "errors" + "golang.org/x/net/proxy" + "net" + "strings" + "time" + "yggdrasil/config" +) + +type Dialer = proxy.Dialer + +type muxedDialer struct { + conf config.NetConfig + tor Dialer + direct Dialer +} + +type wrappedConn struct { + c net.Conn + raddr net.Addr +} + +type wrappedAddr struct { + network string + addr string +} + +func (a *wrappedAddr) Network() string { + return a.network +} + +func (a *wrappedAddr) String() string { + return a.addr +} + +func (c *wrappedConn) Write(data []byte) (int, error) { + return c.c.Write(data) +} + +func (c *wrappedConn) Read(data []byte) (int, error) { + return c.c.Read(data) +} + +func (c *wrappedConn) SetDeadline(t time.Time) error { + return c.c.SetDeadline(t) +} + +func (c *wrappedConn) SetReadDeadline(t time.Time) error { + return c.c.SetReadDeadline(t) +} + +func (c *wrappedConn) SetWriteDeadline(t time.Time) error { + return c.c.SetWriteDeadline(t) +} + +func (c *wrappedConn) Close() error { + return c.c.Close() +} + +func (c *wrappedConn) LocalAddr() net.Addr { + return c.c.LocalAddr() +} + +func (c *wrappedConn) RemoteAddr() net.Addr { + return c.raddr +} + +func (d *muxedDialer) Dial(network, addr string) (net.Conn, error) { + host, _, _ := net.SplitHostPort(addr) + if d.conf.Tor.UseForAll || strings.HasSuffix(host, ".onion") { + if !d.conf.Tor.Enabled { + return nil, errors.New("tor not enabled") + } + c, err := d.tor.Dial(network, addr) + if err == nil { + c = &wrappedConn{ + c: c, + raddr: &wrappedAddr{ + network: network, + addr: addr, + }, + } + } + return c, err + } else { + return d.direct.Dial(network, addr) + } +} + +func NewDialer(c config.NetConfig) Dialer { + tor, _ := proxy.SOCKS5("tcp", c.Tor.SocksAddr, nil, proxy.Direct) + return &muxedDialer{ + conf: c, + tor: tor, + direct: proxy.Direct, + } +} diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 972d1ca..23fa712 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -19,9 +19,17 @@ import "bufio" const tcp_msgSize = 2048 + 65535 // TODO figure out what makes sense +// wrapper function for non tcp/ip connections +func setNoDelay(c net.Conn, delay bool) { + tcp, ok := c.(*net.TCPConn) + if ok { + tcp.SetNoDelay(delay) + } +} + type tcpInterface struct { core *Core - serv *net.TCPListener + serv net.Listener mutex sync.Mutex // Protecting the below calls map[string]struct{} conns map[tcpInfo](chan struct{}) @@ -30,30 +38,27 @@ type tcpInterface struct { type tcpInfo struct { box boxPubKey sig sigPubKey - localAddr string // net.IPAddr.String(), not TCPAddr, don't care about port - remoteAddr string + localAddr net.Addr + remoteAddr net.Addr } -func (iface *tcpInterface) init(core *Core, addr string) { +func (iface *tcpInterface) init(core *Core, addr string) (err error) { iface.core = core - tcpAddr, err := net.ResolveTCPAddr("tcp", addr) - if err != nil { - panic(err) + + iface.serv, err = net.Listen("tcp", addr) + if err == nil { + iface.calls = make(map[string]struct{}) + iface.conns = make(map[tcpInfo](chan struct{})) + go iface.listener() } - iface.serv, err = net.ListenTCP("tcp", tcpAddr) - if err != nil { - panic(err) - } - iface.calls = make(map[string]struct{}) - iface.conns = make(map[tcpInfo](chan struct{})) - go iface.listener() + return } func (iface *tcpInterface) listener() { defer iface.serv.Close() iface.core.log.Println("Listening for TCP on:", iface.serv.Addr().String()) for { - sock, err := iface.serv.AcceptTCP() + sock, err := iface.serv.Accept() if err != nil { panic(err) } @@ -77,17 +82,16 @@ func (iface *tcpInterface) call(saddr string) { } iface.mutex.Unlock() if !quit { - conn, err := net.DialTimeout("tcp", saddr, 6*time.Second) + conn, err := iface.core.Dialer.Dial("tcp", saddr) if err != nil { return } - sock := conn.(*net.TCPConn) - iface.handler(sock) + iface.handler(conn) } }() } -func (iface *tcpInterface) handler(sock *net.TCPConn) { +func (iface *tcpInterface) handler(sock net.Conn) { defer sock.Close() // Get our keys keys := []byte{} @@ -127,18 +131,8 @@ func (iface *tcpInterface) handler(sock *net.TCPConn) { return } // Check if we already have a connection to this node, close and block if yes - local := sock.LocalAddr().(*net.TCPAddr) - laddr := net.IPAddr{ - IP: local.IP, - Zone: local.Zone, - } - info.localAddr = laddr.String() - remote := sock.RemoteAddr().(*net.TCPAddr) - raddr := net.IPAddr{ - IP: remote.IP, - Zone: remote.Zone, - } - info.remoteAddr = raddr.String() + info.localAddr = sock.LocalAddr() + info.remoteAddr = sock.RemoteAddr() iface.mutex.Lock() if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() @@ -224,7 +218,7 @@ func (iface *tcpInterface) handler(sock *net.TCPConn) { util_putBytes(msg) } } - sock.SetNoDelay(true) + setNoDelay(sock, true) go p.linkLoop(linkIn) defer func() { // Put all of our cleanup here... @@ -239,7 +233,7 @@ func (iface *tcpInterface) handler(sock *net.TCPConn) { p.core.peers.mutex.Unlock() close(linkIn) }() - them := sock.RemoteAddr().(*net.TCPAddr) + them := sock.RemoteAddr() themNodeID := getNodeID(&info.box) themAddr := address_addrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() @@ -250,7 +244,7 @@ func (iface *tcpInterface) handler(sock *net.TCPConn) { return } -func (iface *tcpInterface) reader(sock *net.TCPConn, in func([]byte)) { +func (iface *tcpInterface) reader(sock net.Conn, in func([]byte)) { bs := make([]byte, 2*tcp_msgSize) frag := bs[:0] for { diff --git a/yggdrasil.go b/yggdrasil.go index c52964f..85328fc 100644 --- a/yggdrasil.go +++ b/yggdrasil.go @@ -21,31 +21,13 @@ import "runtime" import "golang.org/x/net/ipv6" -import . "yggdrasil" +import "yggdrasil" +import "yggdrasil/config" import "github.com/kardianos/minwinsvc" -/** -* This is a very crude wrapper around src/yggdrasil -* It can generate a new config (--genconf) -* It can read a config from stdin (--useconf) -* It can run with an automatic config (--autoconf) - */ - -type nodeConfig struct { - Listen string - AdminListen string - Peers []string - BoxPub string - BoxPriv string - SigPub string - SigPriv string - Multicast bool - LinkLocal string - IfName string - IfTAPMode bool - IfMTU int -} +type nodeConfig = config.NodeConfig +type Core = yggdrasil.Core type node struct { core Core @@ -76,6 +58,9 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { panic(err) } n.core.DEBUG_setIfceExpr(ifceExpr) + + n.core.Dialer = yggdrasil.NewDialer(cfg.Net) + logger.Println("Starting interface...") n.core.DEBUG_setupAndStartGlobalTCPInterface(cfg.Listen) // Listen for peers on TCP n.core.DEBUG_setupAndStartGlobalUDPInterface(cfg.Listen) // Also listen on UDP, TODO allow separate configuration for ip/port to listen on each of these @@ -126,6 +111,10 @@ func generateConfig(isAutoconf bool) *nodeConfig { cfg.IfName = core.DEBUG_GetTUNDefaultIfName() cfg.IfMTU = core.DEBUG_GetTUNDefaultIfMTU() cfg.IfTAPMode = core.DEBUG_GetTUNDefaultIfTAPMode() + + cfg.Net.Tor.SocksAddr = "127.0.0.1:9050" + cfg.Net.Tor.UseForAll = false + cfg.Net.Tor.Enabled = true return &cfg }