diff --git a/go.mod b/go.mod index bf8e7571..dd5b9626 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,14 @@ require ( github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 + github.com/miekg/dns v1.1.27 // indirect github.com/mitchellh/mapstructure v1.1.2 + github.com/neilalexander/mdns v0.3.1-0.20200509190551-a13500818a93 github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d golang.org/x/net v0.0.0-20200301022130-244492dfa37a + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de golang.zx2c4.com/wireguard v0.0.20200320 diff --git a/go.sum b/go.sum index f67b6806..1b25956d 100644 --- a/go.sum +++ b/go.sum @@ -25,24 +25,40 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= +github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/neilalexander/mdns v0.3.1-0.20200509190551-a13500818a93 h1:XjPDiOX2MoRURK2OOa+5DeI6SVK+4WQdwXV3OI2C67Q= +github.com/neilalexander/mdns v0.3.1-0.20200509190551-a13500818a93/go.mod h1:aJ6mgvEacXD+y9JGp8jMaF8wcsTuSvR4YMU6agygcPQ= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= 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= +golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -55,6 +71,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de h1:aYKJLPSrddB2N7/6OKyFqJ337SXpo61bBuvO5p1+7iY= golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.20200122-0.20200214175355-9cbcff10dd3e/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g= golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY= diff --git a/src/config/config.go b/src/config/config.go index 95d9bbd1..7b07fd59 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -67,6 +67,7 @@ type NodeConfig struct { Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntcp://0.0.0.0:0 or tcp://[::]:0 to listen on all interfaces."` AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."` MulticastInterfaces []string `comment:"Regular expressions for which interfaces multicast peer discovery\nshould be enabled on. If none specified, multicast peer discovery is\ndisabled. The default value is .* which uses all interfaces."` + MulticastDNSInterfaces []string `comment:"Regular expressions for which interfaces mDNS peer discovery\nshould be enabled on. If none specified, mDNS peer discovery is\ndisabled. The default value is .* which uses all interfaces."` AllowedEncryptionPublicKeys []string `comment:"List of peer encryption public keys to allow incoming TCP peering\nconnections from. If left empty/undefined then all connections will\nbe allowed by default. This does not affect outgoing peerings, nor\ndoes it affect link-local peers discovered via multicast."` EncryptionPublicKey string `comment:"Your public encryption key. Your peers may ask you for this to put\ninto their AllowedEncryptionPublicKeys configuration."` EncryptionPrivateKey string `comment:"Your private encryption key. DO NOT share this with anyone!"` @@ -127,6 +128,7 @@ func GenerateConfig() *NodeConfig { cfg.InterfacePeers = map[string][]string{} cfg.AllowedEncryptionPublicKeys = []string{} cfg.MulticastInterfaces = defaults.GetDefaults().DefaultMulticastInterfaces + cfg.MulticastDNSInterfaces = defaults.GetDefaults().DefaultMulticastDNSInterfaces cfg.IfName = defaults.GetDefaults().DefaultIfName cfg.IfMTU = defaults.GetDefaults().DefaultIfMTU cfg.SessionFirewall.Enable = false diff --git a/src/defaults/defaults.go b/src/defaults/defaults.go index a0d372e4..5f0862a6 100644 --- a/src/defaults/defaults.go +++ b/src/defaults/defaults.go @@ -13,7 +13,8 @@ type platformDefaultParameters struct { DefaultConfigFile string // Multicast interfaces - DefaultMulticastInterfaces []string + DefaultMulticastInterfaces []string + DefaultMulticastDNSInterfaces []string // TUN/TAP MaximumIfMTU types.MTU diff --git a/src/defaults/defaults_darwin.go b/src/defaults/defaults_darwin.go index 9fbe6d8e..33221007 100644 --- a/src/defaults/defaults_darwin.go +++ b/src/defaults/defaults_darwin.go @@ -17,6 +17,10 @@ func GetDefaults() platformDefaultParameters { "en.*", "bridge.*", }, + DefaultMulticastDNSInterfaces: []string{ + "en.*", + "bridge.*", + }, // TUN/TAP MaximumIfMTU: 65535, diff --git a/src/defaults/defaults_freebsd.go b/src/defaults/defaults_freebsd.go index b08d80d0..37272249 100644 --- a/src/defaults/defaults_freebsd.go +++ b/src/defaults/defaults_freebsd.go @@ -16,6 +16,9 @@ func GetDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []string{ ".*", }, + DefaultMulticastDNSInterfaces: []string{ + ".*", + }, // TUN/TAP MaximumIfMTU: 32767, diff --git a/src/defaults/defaults_linux.go b/src/defaults/defaults_linux.go index 5f3f12a8..3b388c9f 100644 --- a/src/defaults/defaults_linux.go +++ b/src/defaults/defaults_linux.go @@ -16,6 +16,9 @@ func GetDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []string{ ".*", }, + DefaultMulticastDNSInterfaces: []string{ + ".*", + }, // TUN/TAP MaximumIfMTU: 65535, diff --git a/src/defaults/defaults_openbsd.go b/src/defaults/defaults_openbsd.go index 20741ee8..ad8fd285 100644 --- a/src/defaults/defaults_openbsd.go +++ b/src/defaults/defaults_openbsd.go @@ -16,6 +16,9 @@ func GetDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []string{ ".*", }, + DefaultMulticastDNSInterfaces: []string{ + ".*", + }, // TUN/TAP MaximumIfMTU: 16384, diff --git a/src/defaults/defaults_other.go b/src/defaults/defaults_other.go index 3b035537..03c16ab6 100644 --- a/src/defaults/defaults_other.go +++ b/src/defaults/defaults_other.go @@ -16,6 +16,9 @@ func GetDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []string{ ".*", }, + DefaultMulticastDNSInterfaces: []string{ + ".*", + }, // TUN/TAP MaximumIfMTU: 65535, diff --git a/src/defaults/defaults_windows.go b/src/defaults/defaults_windows.go index 305a2ffe..d3e79b2f 100644 --- a/src/defaults/defaults_windows.go +++ b/src/defaults/defaults_windows.go @@ -16,6 +16,9 @@ func GetDefaults() platformDefaultParameters { DefaultMulticastInterfaces: []string{ ".*", }, + DefaultMulticastDNSInterfaces: []string{ + ".*", + }, // TUN/TAP MaximumIfMTU: 65535, diff --git a/src/mdns/mdns.go b/src/mdns/mdns.go index 8515f6b1..950c25ec 100644 --- a/src/mdns/mdns.go +++ b/src/mdns/mdns.go @@ -1,7 +1,6 @@ package mdns import ( - "context" "encoding/hex" "errors" "fmt" @@ -11,7 +10,7 @@ import ( "github.com/Arceliar/phony" "github.com/gologme/log" - "github.com/grandcat/zeroconf" + "github.com/neilalexander/mdns" "github.com/yggdrasil-network/yggdrasil-go/src/admin" "github.com/yggdrasil-network/yggdrasil-go/src/config" @@ -25,23 +24,22 @@ const ( type MDNS struct { phony.Inbox - core *yggdrasil.Core // - config *config.NodeState // - log *log.Logger // - info []string // - instance string // - _running bool // - _exprs []*regexp.Regexp // - _servers map[string]map[string]*mDNSServer // intf -> ip -> *mDNSServer - _resolver *zeroconf.Resolver // - _context context.Context // used by _resolver - _cancel context.CancelFunc // used by _resolver + core *yggdrasil.Core // + config *config.NodeState // + log *log.Logger // + info []string // + instance string // + _running bool // is mDNS running? + _exprs []*regexp.Regexp // mDNS interfaces + _servers map[string]map[string]*mDNSServer // intf -> ip -> *mDNSServer } type mDNSServer struct { + mdns *MDNS intf net.Interface - server *zeroconf.Server + server *mdns.Server listener *yggdrasil.TcpListener + stop chan struct{} time time.Time } @@ -62,20 +60,9 @@ func (m *MDNS) Init(core *yggdrasil.Core, state *config.NodeState, log *log.Logg current := m.config.GetCurrent() m._updateConfig(¤t) - m._context, m._cancel = context.WithCancel(context.Background()) - m._servers = make(map[string]map[string]*mDNSServer) - return nil } -func (m *MDNS) listen(results <-chan *zeroconf.ServiceEntry) { - m.log.Info("Listening for other Yggdrasil nodes via mDNS") - for entry := range results { - log.Info("Received mDNS entry:", entry) - } - m.log.Info("No longer listening for other Yggdrasil nodes") -} - func (m *MDNS) Start() error { var err error phony.Block(m, func() { @@ -86,25 +73,13 @@ func (m *MDNS) Start() error { } func (m *MDNS) _start() error { - var err error if m._running { return errors.New("mDNS module is already running") } - resolver, err := zeroconf.NewResolver(nil) - if err != nil { - m.log.Fatalln("Failed to initialize resolver:", err.Error()) - } - m._resolver = resolver - incoming := make(chan *zeroconf.ServiceEntry) - go m.listen(incoming) - - err = m._resolver.Browse(m._context, MDNSService, MDNSDomain, incoming) - if err != nil { - m.log.Fatalln("Failed to browse:", err.Error()) - } - + m._servers = make(map[string]map[string]*mDNSServer) m._running = true + m.Act(m, m._updateInterfaces) return nil @@ -120,7 +95,6 @@ func (m *MDNS) Stop() error { } func (m *MDNS) _stop() error { - m._cancel() for _, intf := range m._servers { for _, ip := range intf { ip.server.Shutdown() @@ -148,7 +122,7 @@ func (m *MDNS) _updateConfig(config *config.NodeConfig) error { // allowed to use. var exprs []*regexp.Regexp // Compile each regular expression - for _, exstr := range config.MulticastInterfaces { + for _, exstr := range config.MulticastDNSInterfaces { e, err := regexp.Compile(exstr) if err != nil { return err @@ -230,7 +204,7 @@ func (m *MDNS) _updateInterfaces() { } // Work out which interfaces have disappeared. - for n, _ := range m._servers { + for n := range m._servers { if addrs, ok := interfaces[n]; !ok { for addr, server := range m._servers[n] { if err := m._stopInterface(server.intf, addr); err != nil { @@ -277,20 +251,28 @@ func (m *MDNS) _startInterface(intf net.Interface, addr string) error { return fmt.Errorf("net.ResolveTCPAddr: %w", err) } - // Register a proxy service. This allows us to specify the hostname and - // IP addresses to put into the DNS SRV record. - server, err := zeroconf.RegisterProxy( - m.instance, // instance name - MDNSService, // service name - MDNSDomain, // service domain - tcpaddr.Port, // TCP listener port - m.instance, // our hostname - []string{tcpaddr.IP.String()}, // our IP address - m.info, // TXT record contents - []net.Interface{intf}, // interfaces to use + // Create a zone. + hostname := fmt.Sprintf("%s.%s", m.instance, MDNSDomain) + zone, err := mdns.NewMDNSService( + m.instance, // instance name + MDNSService, // service name + MDNSDomain, // service domain + hostname, // our hostname + tcpaddr.Port, // TCP listener port + []net.IP{tcpaddr.IP}, // our IP address + m.info, // TXT record contents ) if err != nil { - return fmt.Errorf("zeroconf.RegisterProxy: %w", err) + return fmt.Errorf("mdns.NewMDNSService: %w", err) + } + + // Create a server. + server, err := mdns.NewServer(&mdns.Config{ + Zone: zone, + Iface: &intf, + }) + if err != nil { + return fmt.Errorf("mdns.NewServer: %w", err) } // Now store information about our new listener and server. @@ -298,10 +280,13 @@ func (m *MDNS) _startInterface(intf net.Interface, addr string) error { m._servers[intf.Name] = make(map[string]*mDNSServer) } m._servers[intf.Name][addr] = &mDNSServer{ + mdns: m, intf: intf, + stop: make(chan struct{}), server: server, listener: listener, } + go m._servers[intf.Name][addr].listen() return nil } @@ -320,6 +305,7 @@ func (m *MDNS) _stopInterface(intf net.Interface, addr string) error { } // Shut down the mDNS server and the TCP listener. + close(server.stop) server.server.Shutdown() server.listener.Stop() @@ -331,3 +317,30 @@ func (m *MDNS) _stopInterface(intf net.Interface, addr string) error { return nil } + +func (s *mDNSServer) listen() { + s.mdns.log.Debugln("Started listening for mDNS on", s.intf.Name) + incoming := make(chan *mdns.ServiceEntry) + + go func() { + if err := mdns.Listen(incoming, s.stop, &s.intf); err != nil { + s.mdns.log.Errorln("Failed to initialize resolver:", err.Error()) + } + }() + + for { + select { + case <-s.stop: + s.mdns.log.Debugln("Stopped listening for mDNS on", s.intf.Name) + break + case entry := <-incoming: + if entry.AddrV6.IP.IsLinkLocalUnicast() && entry.AddrV6.Zone == "" { + continue + } + addr := fmt.Sprintf("tcp://%s:%d", entry.AddrV6.IP, entry.Port) + if err := s.mdns.core.CallPeer(addr, entry.AddrV6.Zone); err != nil { + s.mdns.log.Warn("Failed to add peer from mDNS: ", err) + } + } + } +}