diff --git a/README.md b/README.md index dcc9bd91..aa4a46c1 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ some of the below: - Linux - `.deb` and `.rpm` packages are built by CI for Debian and Red Hat-based distributions - - Void and Arch packages also available within their respective repositories + - Arch, Nix, Void packages also available within their respective repositories - macOS - `.pkg` packages are built by CI - Ubiquiti EdgeOS diff --git a/go.mod b/go.mod index 04cd5b90..cea62b2b 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,12 @@ require ( github.com/cheggaaa/pb/v3 v3.0.4 github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 - github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible + github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/mitchellh/mapstructure v1.1.2 github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect + github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855 // indirect golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d golang.org/x/net v0.0.0-20200301022130-244492dfa37a golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 diff --git a/go.sum b/go.sum index 6d08e865..013abbeb 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTu github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:bLQ2Ve+eW65id3b8xEMQiAwJT4qGZeywAEMLvXjznvw= -github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= +github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible h1:v6BPcb9q9U6JDVsuizxBr/piVB/2Y1Q5GWoBybvZVWI= +github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1 h1:/QwQcwWVOQXcoNuV9tHx30gQ3q7jCE/rKcGjwzsa5tg= @@ -31,6 +31,8 @@ github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCO github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855 h1:xLQihK8bAKOEDii/Z39dHTgSJzetm2TQ1YKRPRX87R4= +github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855/go.mod h1:xQdsh08Io6nV4WRnOVTe6gI8/2iTvfLDQ0CYa5aMt+I= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= diff --git a/src/admin/admin.go b/src/admin/admin.go index 225f9dd6..a7c651c9 100644 --- a/src/admin/admin.go +++ b/src/admin/admin.go @@ -188,17 +188,44 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) { }, }, errors.New("Failed to add peer") }) - a.AddHandler("removePeer", []string{"port"}, func(in Info) (Info, error) { + a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) { port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64) if err != nil { return Info{}, err } if a.core.DisconnectPeer(uint64(port)) == nil { return Info{ - "removed": []string{ + "disconnected": []string{ fmt.Sprint(port), }, }, nil + } else { + return Info{ + "not_disconnected": []string{ + fmt.Sprint(port), + }, + }, errors.New("Failed to disconnect peer") + } + }) + a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) { + // Set sane defaults + intf := "" + // Has interface been specified? + if itf, ok := in["interface"]; ok { + intf = itf.(string) + } + if a.core.RemovePeer(in["uri"].(string), intf) == nil { + return Info{ + "removed": []string{ + in["uri"].(string), + }, + }, nil + } else { + return Info{ + "not_removed": []string{ + in["uri"].(string), + }, + }, errors.New("Failed to remove peer") } return Info{ "not_removed": []string{ diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index b08d80d0..a57be2fa 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -10,7 +10,7 @@ func GetDefaults() platformDefaultParameters { DefaultAdminListen: "unix:///var/run/yggdrasil.sock", // Configuration (used for yggdrasilctl) - DefaultConfigFile: "/etc/yggdrasil.conf", + DefaultConfigFile: "/usr/local/etc/yggdrasil.conf", // Multicast interfaces DefaultMulticastInterfaces: []string{ diff --git a/src/multicast/multicast.go b/src/multicast/multicast.go index 93bf94bb..33cb29ab 100644 --- a/src/multicast/multicast.go +++ b/src/multicast/multicast.go @@ -21,16 +21,20 @@ import ( // automatically. type Multicast struct { phony.Inbox - core *yggdrasil.Core - config *config.NodeState - log *log.Logger - sock *ipv6.PacketConn - groupAddr string - listeners map[string]*listenerInfo - listenPort uint16 - isOpen bool - announcer *time.Timer - platformhandler *time.Timer + core *yggdrasil.Core + config *config.NodeState + log *log.Logger + sock *ipv6.PacketConn + groupAddr string + listeners map[string]*listenerInfo + listenPort uint16 + isOpen bool + _interfaces map[string]interfaceInfo +} + +type interfaceInfo struct { + iface net.Interface + addrs []net.Addr } type listenerInfo struct { @@ -45,6 +49,7 @@ func (m *Multicast) Init(core *yggdrasil.Core, state *config.NodeState, log *log m.config = state m.log = log m.listeners = make(map[string]*listenerInfo) + m._interfaces = make(map[string]interfaceInfo) current := m.config.GetCurrent() m.listenPort = current.LinkLocalTCPPort m.groupAddr = "[ff02::114]:9001" @@ -90,8 +95,8 @@ func (m *Multicast) _start() error { m.isOpen = true go m.listen() - m.Act(m, m.multicastStarted) - m.Act(m, m.announce) + m.Act(nil, m._multicastStarted) + m.Act(nil, m._announce) return nil } @@ -118,12 +123,6 @@ func (m *Multicast) Stop() error { func (m *Multicast) _stop() error { m.log.Infoln("Stopping multicast module") m.isOpen = false - if m.announcer != nil { - m.announcer.Stop() - } - if m.platformhandler != nil { - m.platformhandler.Stop() - } if m.sock != nil { m.sock.Close() } @@ -134,7 +133,7 @@ func (m *Multicast) _stop() error { // and then signals the various module goroutines to reconfigure themselves if // needed. func (m *Multicast) UpdateConfig(config *config.NodeConfig) { - m.Act(m, func() { m._updateConfig(config) }) + m.Act(nil, func() { m._updateConfig(config) }) } func (m *Multicast) _updateConfig(config *config.NodeConfig) { @@ -156,10 +155,35 @@ func (m *Multicast) _updateConfig(config *config.NodeConfig) { m.log.Debugln("Reloaded multicast configuration successfully") } -// GetInterfaces returns the currently known/enabled multicast interfaces. It is -// expected that UpdateInterfaces has been called at least once before calling -// this method. +func (m *Multicast) _updateInterfaces() { + interfaces := make(map[string]interfaceInfo) + intfs := m.getAllowedInterfaces() + for _, intf := range intfs { + addrs, err := intf.Addrs() + if err != nil { + m.log.Warnf("Failed up get addresses for interface %s: %s", intf.Name, err) + continue + } + interfaces[intf.Name] = interfaceInfo{ + iface: intf, + addrs: addrs, + } + } + m._interfaces = interfaces +} + func (m *Multicast) Interfaces() map[string]net.Interface { + interfaces := make(map[string]net.Interface) + phony.Block(m, func() { + for _, info := range m._interfaces { + interfaces[info.iface.Name] = info.iface + } + }) + return interfaces +} + +// getAllowedInterfaces returns the currently known/enabled multicast interfaces. +func (m *Multicast) getAllowedInterfaces() map[string]net.Interface { interfaces := make(map[string]net.Interface) // Get interface expressions from config current := m.config.GetCurrent() @@ -198,7 +222,11 @@ func (m *Multicast) Interfaces() map[string]net.Interface { return interfaces } -func (m *Multicast) announce() { +func (m *Multicast) _announce() { + if !m.isOpen { + return + } + m._updateInterfaces() groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) if err != nil { panic(err) @@ -207,7 +235,6 @@ func (m *Multicast) announce() { if err != nil { panic(err) } - interfaces := m.Interfaces() // There might be interfaces that we configured listeners for but are no // longer up - if that's the case then we should stop the listeners for name, info := range m.listeners { @@ -219,7 +246,7 @@ func (m *Multicast) announce() { } // If the interface is no longer visible on the system then stop the // listener, as another one will be started further down - if _, ok := interfaces[name]; !ok { + if _, ok := m._interfaces[name]; !ok { stop() continue } @@ -232,17 +259,13 @@ func (m *Multicast) announce() { continue } // Find the interface that matches the listener - if intf, err := net.InterfaceByName(name); err == nil { - if addrs, err := intf.Addrs(); err == nil { - // Loop through the addresses attached to that listener and see if any - // of them match the current address of the listener - for _, addr := range addrs { - if ip, _, err := net.ParseCIDR(addr.String()); err == nil { - // Does the interface address match our listener address? - if ip.Equal(listenaddr.IP) { - found = true - break - } + if info, ok := m._interfaces[name]; ok { + for _, addr := range info.addrs { + if ip, _, err := net.ParseCIDR(addr.String()); err == nil { + // Does the interface address match our listener address? + if ip.Equal(listenaddr.IP) { + found = true + break } } } @@ -256,13 +279,9 @@ func (m *Multicast) announce() { } // Now that we have a list of valid interfaces from the operating system, // we can start checking if we can send multicasts on them - for _, iface := range interfaces { - // Find interface addresses - addrs, err := iface.Addrs() - if err != nil { - panic(err) - } - for _, addr := range addrs { + for _, info := range m._interfaces { + iface := info.iface + for _, addr := range info.addrs { addrIP, _, _ := net.ParseCIDR(addr.String()) // Ignore IPv4 addresses if addrIP.To4() != nil { @@ -312,8 +331,8 @@ func (m *Multicast) announce() { break } } - m.announcer = time.AfterFunc(time.Second, func() { - m.Act(m, m.announce) + time.AfterFunc(time.Second, func() { + m.Act(nil, m._announce) }) } @@ -351,7 +370,11 @@ func (m *Multicast) listen() { if addr.IP.String() != from.IP.String() { continue } - if _, ok := m.Interfaces()[from.Zone]; ok { + var interfaces map[string]interfaceInfo + phony.Block(m, func() { + interfaces = m._interfaces + }) + if _, ok := interfaces[from.Zone]; ok { addr.Zone = "" if err := m.core.CallPeer("tcp://"+addr.String(), from.Zone); err != nil { m.log.Debugln("Call from multicast failed:", err) diff --git a/src/multicast/multicast_darwin.go b/src/multicast/multicast_darwin.go index 4cfef9e9..ceff5b44 100644 --- a/src/multicast/multicast_darwin.go +++ b/src/multicast/multicast_darwin.go @@ -31,16 +31,19 @@ import ( var awdlGoroutineStarted bool -func (m *Multicast) multicastStarted() { +func (m *Multicast) _multicastStarted() { + if !m.isOpen { + return + } C.StopAWDLBrowsing() - for intf := range m.Interfaces() { + for intf := range m._interfaces { if intf == "awdl0" { C.StartAWDLBrowsing() break } } - m.platformhandler = time.AfterFunc(time.Minute, func() { - m.Act(m, m.multicastStarted) + time.AfterFunc(time.Minute, func() { + m.Act(nil, m._multicastStarted) }) } diff --git a/src/multicast/multicast_other.go b/src/multicast/multicast_other.go index 16ea15d6..dfcf625f 100644 --- a/src/multicast/multicast_other.go +++ b/src/multicast/multicast_other.go @@ -4,7 +4,7 @@ package multicast import "syscall" -func (m *Multicast) multicastStarted() { +func (m *Multicast) _multicastStarted() { } diff --git a/src/multicast/multicast_unix.go b/src/multicast/multicast_unix.go index 9d6a01a8..1ff48b17 100644 --- a/src/multicast/multicast_unix.go +++ b/src/multicast/multicast_unix.go @@ -5,7 +5,7 @@ package multicast import "syscall" import "golang.org/x/sys/unix" -func (m *Multicast) multicastStarted() { +func (m *Multicast) _multicastStarted() { } diff --git a/src/multicast/multicast_windows.go b/src/multicast/multicast_windows.go index 7a846b1e..3666faaa 100644 --- a/src/multicast/multicast_windows.go +++ b/src/multicast/multicast_windows.go @@ -5,7 +5,7 @@ package multicast import "syscall" import "golang.org/x/sys/windows" -func (m *Multicast) multicastStarted() { +func (m *Multicast) _multicastStarted() { } diff --git a/src/yggdrasil/api.go b/src/yggdrasil/api.go index 82d0aa93..be905cbc 100644 --- a/src/yggdrasil/api.go +++ b/src/yggdrasil/api.go @@ -468,12 +468,31 @@ func (c *Core) AddPeer(addr string, sintf string) error { return nil } -// RemovePeer is not implemented yet. func (c *Core) RemovePeer(addr string, sintf string) error { - // TODO: Implement a reverse of AddPeer, where we look up the port number - // based on the addr and sintf, disconnect it and then remove it from the - // peers list so we don't reconnect to it later - return errors.New("not implemented") + if sintf == "" { + for i, peer := range c.config.Current.Peers { + if peer == addr { + c.config.Current.Peers = append(c.config.Current.Peers[:i], c.config.Current.Peers[i+1:]...) + break + } + } + } else if _, ok := c.config.Current.InterfacePeers[sintf]; ok { + for i, peer := range c.config.Current.InterfacePeers[sintf] { + if peer == addr { + c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf][:i], c.config.Current.InterfacePeers[sintf][i+1:]...) + break + } + } + } + + ports := c.peers.ports.Load().(map[switchPort]*peer) + for p, peer := range ports { + if addr == peer.intf.name { + c.peers.removePeer(p) + } + } + + return nil } // CallPeer calls a peer once. This should be specified in the peer URI format, diff --git a/src/yggdrasil/conn.go b/src/yggdrasil/conn.go index 84f1ddf7..1b7a5a1e 100644 --- a/src/yggdrasil/conn.go +++ b/src/yggdrasil/conn.go @@ -145,7 +145,8 @@ func (c *Conn) search() error { } // Used in session keep-alive traffic -func (c *Conn) doSearch() { +func (c *Conn) _doSearch() { + s := fmt.Sprintf("conn=%p", c) routerWork := func() { // Check to see if there is a search already matching the destination sinfo, isIn := c.core.router.searches.searches[*c.nodeID] @@ -153,7 +154,7 @@ func (c *Conn) doSearch() { // Nothing was found, so create a new search searchCompleted := func(sinfo *sessionInfo, e error) {} sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) - c.core.log.Debugf("%s DHT search started: %p", c.String(), sinfo) + c.core.log.Debugf("%s DHT search started: %p", s, sinfo) // Start the search sinfo.startSearch() } @@ -267,7 +268,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error { case time.Since(c.session.time) > 6*time.Second: if c.session.time.Before(c.session.pingTime) && time.Since(c.session.pingTime) > 6*time.Second { // TODO double check that the above condition is correct - c.doSearch() + c._doSearch() } else { c.session.ping(c.session) // TODO send from self if this becomes an actor } diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 8efc549f..013fd1e4 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -260,7 +260,9 @@ func (t *dht) handleRes(res *dhtRes) { key: res.Key, coords: res.Coords, } - t.insert(&rinfo) + if t.isImportant(&rinfo) { + t.insert(&rinfo) + } for _, info := range res.Infos { if *info.getNodeID() == t.nodeID { continue diff --git a/src/yggdrasil/link.go b/src/yggdrasil/link.go index 53e8c758..01d4aff4 100644 --- a/src/yggdrasil/link.go +++ b/src/yggdrasil/link.go @@ -16,6 +16,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/util" + "golang.org/x/net/proxy" "github.com/Arceliar/phony" ) @@ -50,6 +51,7 @@ type linkInterface struct { name string link *link peer *peer + options linkOptions msgIO linkInterfaceMsgIO info linkInfo incoming bool @@ -67,6 +69,11 @@ type linkInterface struct { unstalled bool // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up) } +type linkOptions struct { + pinnedCurve25519Keys map[crypto.BoxPubKey]struct{} + pinnedEd25519Keys map[crypto.SigPubKey]struct{} +} + func (l *link) init(c *Core) error { l.core = c l.mutex.Lock() @@ -92,13 +99,41 @@ func (l *link) call(uri string, sintf string) error { return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err) } pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") + tcpOpts := tcpOptions{} + if pubkeys, ok := u.Query()["curve25519"]; ok && len(pubkeys) > 0 { + tcpOpts.pinnedCurve25519Keys = make(map[crypto.BoxPubKey]struct{}) + for _, pubkey := range pubkeys { + if boxPub, err := hex.DecodeString(pubkey); err == nil { + var boxPubKey crypto.BoxPubKey + copy(boxPubKey[:], boxPub) + tcpOpts.pinnedCurve25519Keys[boxPubKey] = struct{}{} + } + } + } + if pubkeys, ok := u.Query()["ed25519"]; ok && len(pubkeys) > 0 { + tcpOpts.pinnedEd25519Keys = make(map[crypto.SigPubKey]struct{}) + for _, pubkey := range pubkeys { + if sigPub, err := hex.DecodeString(pubkey); err == nil { + var sigPubKey crypto.SigPubKey + copy(sigPubKey[:], sigPub) + tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{} + } + } + } switch u.Scheme { case "tcp": - l.tcp.call(u.Host, nil, sintf, nil) + l.tcp.call(u.Host, tcpOpts, sintf) case "socks": - l.tcp.call(pathtokens[0], u.Host, sintf, nil) + tcpOpts.socksProxyAddr = u.Host + if u.User != nil { + tcpOpts.socksProxyAuth = &proxy.Auth{} + tcpOpts.socksProxyAuth.User = u.User.Username() + tcpOpts.socksProxyAuth.Password, _ = u.User.Password() + } + l.tcp.call(pathtokens[0], tcpOpts, sintf) case "tls": - l.tcp.call(u.Host, nil, sintf, l.tcp.tls.forDialer) + tcpOpts.upgrade = l.tcp.tls.forDialer + l.tcp.call(u.Host, tcpOpts, sintf) default: return errors.New("unknown call scheme: " + u.Scheme) } @@ -122,12 +157,13 @@ func (l *link) listen(uri string) error { } } -func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) { +func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool, options linkOptions) (*linkInterface, error) { // Technically anything unique would work for names, but let's pick something human readable, just for debugging intf := linkInterface{ - name: name, - link: l, - msgIO: msgIO, + name: name, + link: l, + options: options, + msgIO: msgIO, info: linkInfo{ linkType: linkType, local: local, @@ -181,6 +217,20 @@ func (intf *linkInterface) handler() error { intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) return errors.New("failed to connect: wrong version") } + // Check if the remote side matches the keys we expected. This is a bit of a weak + // check - in future versions we really should check a signature or something like that. + if pinned := intf.options.pinnedCurve25519Keys; pinned != nil { + if _, allowed := pinned[meta.box]; !allowed { + intf.link.core.log.Errorf("Failed to connect to node: %q sent curve25519 key that does not match pinned keys", intf.name) + return fmt.Errorf("failed to connect: host sent curve25519 key that does not match pinned keys") + } + } + if pinned := intf.options.pinnedEd25519Keys; pinned != nil { + if _, allowed := pinned[meta.sig]; !allowed { + intf.link.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name) + return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys") + } + } // Check if we're authorized to connect to this key / IP if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", diff --git a/src/yggdrasil/tcp.go b/src/yggdrasil/tcp.go index d7c321b3..2bc81de1 100644 --- a/src/yggdrasil/tcp.go +++ b/src/yggdrasil/tcp.go @@ -25,6 +25,7 @@ import ( "golang.org/x/net/proxy" + "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/util" ) @@ -57,6 +58,14 @@ type TcpUpgrade struct { name string } +type tcpOptions struct { + linkOptions + upgrade *TcpUpgrade + socksProxyAddr string + socksProxyAuth *proxy.Auth + socksPeerAddr string +} + func (l *TcpListener) Stop() { defer func() { recover() }() close(l.stop) @@ -220,7 +229,10 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) { return } t.waitgroup.Add(1) - go t.handler(sock, true, nil, l.upgrade) + options := tcpOptions{ + upgrade: l.upgrade, + } + go t.handler(sock, true, options) } } @@ -238,12 +250,12 @@ func (t *tcp) startCalling(saddr string) bool { // If the dial is successful, it launches the handler. // When finished, it removes the outgoing call, so reconnection attempts can be made later. // This all happens in a separate goroutine that it spawns. -func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *TcpUpgrade) { +func (t *tcp) call(saddr string, options tcpOptions, sintf string) { go func() { callname := saddr callproto := "TCP" - if upgrade != nil { - callproto = strings.ToUpper(upgrade.name) + if options.upgrade != nil { + callproto = strings.ToUpper(options.upgrade.name) } if sintf != "" { callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf) @@ -262,17 +274,16 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp }() var conn net.Conn var err error - socksaddr, issocks := options.(string) - if issocks { + if options.socksProxyAddr != "" { if sintf != "" { return } - dialerdst, er := net.ResolveTCPAddr("tcp", socksaddr) + dialerdst, er := net.ResolveTCPAddr("tcp", options.socksProxyAddr) if er != nil { return } var dialer proxy.Dialer - dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), nil, proxy.Direct) + dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), options.socksProxyAuth, proxy.Direct) if err != nil { return } @@ -281,7 +292,8 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp return } t.waitgroup.Add(1) - t.handler(conn, false, saddr, nil) + options.socksPeerAddr = conn.RemoteAddr().String() + t.handler(conn, false, options) } else { dst, err := net.ResolveTCPAddr("tcp", saddr) if err != nil { @@ -347,19 +359,19 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp return } t.waitgroup.Add(1) - t.handler(conn, false, nil, upgrade) + t.handler(conn, false, options) } }() } -func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade *TcpUpgrade) { +func (t *tcp) handler(sock net.Conn, incoming bool, options tcpOptions) { defer t.waitgroup.Done() // Happens after sock.close defer sock.Close() t.setExtraOptions(sock) var upgraded bool - if upgrade != nil { + if options.upgrade != nil { var err error - if sock, err = upgrade.upgrade(sock); err != nil { + if sock, err = options.upgrade.upgrade(sock); err != nil { t.link.core.log.Errorln("TCP handler upgrade failed:", err) return } @@ -368,14 +380,14 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade stream := stream{} stream.init(sock) var name, proto, local, remote string - if socksaddr, issocks := options.(string); issocks { - name = "socks://" + sock.RemoteAddr().String() + "/" + socksaddr + if options.socksProxyAddr != "" { + name = "socks://" + sock.RemoteAddr().String() + "/" + options.socksPeerAddr proto = "socks" local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) - remote, _, _ = net.SplitHostPort(socksaddr) + remote, _, _ = net.SplitHostPort(options.socksPeerAddr) } else { if upgraded { - proto = upgrade.name + proto = options.upgrade.name name = proto + "://" + sock.RemoteAddr().String() } else { proto = "tcp" @@ -384,8 +396,21 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) } + localIP := net.ParseIP(local) + if localIP = localIP.To16(); localIP != nil { + var laddr address.Address + var lsubnet address.Subnet + copy(laddr[:], localIP) + copy(lsubnet[:], localIP) + if laddr.IsValid() || lsubnet.IsValid() { + // The local address is with the network address/prefix range + // This would route ygg over ygg, which we don't want + t.link.core.log.Debugln("Dropping ygg-tunneled connection", local, remote) + return + } + } force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast() - link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force) + link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force, options.linkOptions) if err != nil { t.link.core.log.Println(err) panic(err)