diff --git a/src/yggdrasil/config/config.go b/src/yggdrasil/config/config.go new file mode 100644 index 00000000..30c8d23d --- /dev/null +++ b/src/yggdrasil/config/config.go @@ -0,0 +1,24 @@ +package config + +// NodeConfig defines all configuration values needed to run a signle yggdrasil node +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 +} + +// NetConfig defines network/proxy related configuration values +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 00000000..0ee4a2b7 --- /dev/null +++ b/src/yggdrasil/config/i2p.go @@ -0,0 +1,8 @@ +package config + +// I2PConfig is the configuration structure for i2p related configuration +type I2PConfig struct { + Keyfile string // private key file or empty string for ephemeral keys + Addr string // address of i2p api connector + Enabled bool +} diff --git a/src/yggdrasil/config/tor.go b/src/yggdrasil/config/tor.go new file mode 100644 index 00000000..c169cbbe --- /dev/null +++ b/src/yggdrasil/config/tor.go @@ -0,0 +1,8 @@ +package config + +// TorConfig is the configuration structure for Tor Proxy related values +type TorConfig struct { + OnionKeyfile string // hidden service private key for ADD_ONION (currently unimplemented) + ControlAddr string // tor control port address + Enabled bool +} diff --git a/src/yggdrasil/debug.go b/src/yggdrasil/debug.go index d358bc62..faa7471e 100644 --- a/src/yggdrasil/debug.go +++ b/src/yggdrasil/debug.go @@ -8,9 +8,13 @@ package yggdrasil import _ "golang.org/x/net/ipv6" // TODO put this somewhere better +import "golang.org/x/net/proxy" + import "fmt" import "net" +import "net/url" import "log" +import "strings" import "regexp" // Core @@ -307,6 +311,51 @@ func (c *Core) DEBUG_maybeSendUDPKeys(saddr string) { //////////////////////////////////////////////////////////////////////////////// +func (c *Core) DEBUG_addPeer(addr string) { + u, err := url.Parse(addr) + if err == nil { + switch strings.ToLower(u.Scheme) { + case "tcp": + c.DEBUG_addTCPConn(u.Host) + case "udp": + c.DEBUG_maybeSendUDPKeys(u.Host) + case "socks": + c.DEBUG_addSOCKSConn(u.Host, u.Path[1:]) + default: + panic("invalid peer: " + addr) + } + } else { + // no url scheme provided + addr = strings.ToLower(addr) + if strings.HasPrefix(addr, "udp:") { + c.DEBUG_maybeSendUDPKeys(addr[4:]) + } else { + if strings.HasPrefix(addr, "tcp:") { + addr = addr[4:] + } + c.DEBUG_addTCPConn(addr) + } + } +} + +func (c *Core) DEBUG_addSOCKSConn(socksaddr, peeraddr string) { + go func() { + dialer, err := proxy.SOCKS5("tcp", socksaddr, nil, proxy.Direct) + if err == nil { + conn, err := dialer.Dial("tcp", peeraddr) + if err == nil { + c.tcp.callWithConn(&wrappedConn{ + c: conn, + raddr: &wrappedAddr{ + network: "tcp", + addr: peeraddr, + }, + }) + } + } + }() +} + //* func (c *Core) DEBUG_setupAndStartGlobalTCPInterface(addrport string) { iface := tcpInterface{} diff --git a/src/yggdrasil/dial.go b/src/yggdrasil/dial.go new file mode 100644 index 00000000..7aec4192 --- /dev/null +++ b/src/yggdrasil/dial.go @@ -0,0 +1,58 @@ +package yggdrasil + +import ( + "net" + "time" +) + +// wrappedConn implements net.Conn +type wrappedConn struct { + c net.Conn + raddr net.Addr +} + +// wrappedAddr implements 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 +} diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index 972d1ca3..d84dd345 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 + localAddr string remoteAddr string } -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) } @@ -61,6 +66,26 @@ func (iface *tcpInterface) listener() { } } +func (iface *tcpInterface) callWithConn(conn net.Conn) { + go func() { + raddr := conn.RemoteAddr().String() + iface.mutex.Lock() + _, isIn := iface.calls[raddr] + iface.mutex.Unlock() + if !isIn { + iface.mutex.Lock() + iface.calls[raddr] = struct{}{} + iface.mutex.Unlock() + defer func() { + iface.mutex.Lock() + delete(iface.calls, raddr) + iface.mutex.Unlock() + }() + iface.handler(conn) + } + }() +} + func (iface *tcpInterface) call(saddr string) { go func() { quit := false @@ -77,17 +102,16 @@ func (iface *tcpInterface) call(saddr string) { } iface.mutex.Unlock() if !quit { - conn, err := net.DialTimeout("tcp", saddr, 6*time.Second) + conn, err := net.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 +151,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, _, _ = net.SplitHostPort(sock.LocalAddr().String()) + info.remoteAddr, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) iface.mutex.Lock() if blockChan, isIn := iface.conns[info]; isIn { iface.mutex.Unlock() @@ -224,7 +238,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 +253,7 @@ func (iface *tcpInterface) handler(sock *net.TCPConn) { p.core.peers.mutex.Unlock() close(linkIn) }() - them := sock.RemoteAddr().(*net.TCPAddr) + them, _, _ := net.SplitHostPort(sock.RemoteAddr().String()) themNodeID := getNodeID(&info.box) themAddr := address_addrForNodeID(themNodeID) themAddrString := net.IP(themAddr[:]).String() @@ -250,7 +264,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 c52964f0..30c6a798 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,7 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { panic(err) } n.core.DEBUG_setIfceExpr(ifceExpr) + 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 @@ -89,14 +72,7 @@ func (n *node) init(cfg *nodeConfig, logger *log.Logger) { } for { for _, p := range cfg.Peers { - switch { - case len(p) >= 4 && p[:4] == "udp:": - n.core.DEBUG_maybeSendUDPKeys(p[4:]) - case len(p) >= 4 && p[:4] == "tcp:": - n.core.DEBUG_addTCPConn(p[4:]) - default: - n.core.DEBUG_addTCPConn(p) - } + n.core.DEBUG_addPeer(p) time.Sleep(time.Second) } time.Sleep(time.Minute) @@ -126,6 +102,7 @@ func generateConfig(isAutoconf bool) *nodeConfig { cfg.IfName = core.DEBUG_GetTUNDefaultIfName() cfg.IfMTU = core.DEBUG_GetTUNDefaultIfMTU() cfg.IfTAPMode = core.DEBUG_GetTUNDefaultIfTAPMode() + return &cfg }