diff --git a/.licenses/mdns-discovery/go/github.com/brutella/dnssd.dep.yml b/.licenses/mdns-discovery/go/github.com/brutella/dnssd.dep.yml deleted file mode 100644 index f612561..0000000 --- a/.licenses/mdns-discovery/go/github.com/brutella/dnssd.dep.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: github.com/brutella/dnssd -version: v1.1.1 -type: go -summary: -homepage: https://pkg.go.dev/github.com/brutella/dnssd -license: mit -licenses: -- sources: LICENSE - text: |- - The MIT License (MIT) - - Copyright (c) 2017 Matthias Hochgatterer - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -- sources: README.md - text: "*dnssd* is available under the MIT license. See the LICENSE file for more - info." -notices: [] diff --git a/.licenses/mdns-discovery/go/github.com/brutella/dnssd/log.dep.yml b/.licenses/mdns-discovery/go/github.com/brutella/dnssd/log.dep.yml deleted file mode 100644 index 5466740..0000000 --- a/.licenses/mdns-discovery/go/github.com/brutella/dnssd/log.dep.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: github.com/brutella/dnssd/log -version: v1.1.1 -type: go -summary: -homepage: https://pkg.go.dev/github.com/brutella/dnssd/log -license: mit -licenses: -- sources: dnssd@v1.1.1/LICENSE - text: |- - The MIT License (MIT) - - Copyright (c) 2017 Matthias Hochgatterer - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -- sources: dnssd@v1.1.1/README.md - text: "*dnssd* is available under the MIT license. See the LICENSE file for more - info." -notices: [] diff --git a/.licenses/mdns-discovery/go/github.com/hashicorp/mdns.dep.yml b/.licenses/mdns-discovery/go/github.com/hashicorp/mdns.dep.yml new file mode 100644 index 0000000..0f9bd14 --- /dev/null +++ b/.licenses/mdns-discovery/go/github.com/hashicorp/mdns.dep.yml @@ -0,0 +1,31 @@ +--- +name: github.com/hashicorp/mdns +version: v1.0.4 +type: go +summary: +homepage: https://pkg.go.dev/github.com/hashicorp/mdns +license: mit +licenses: +- sources: LICENSE + text: | + The MIT License (MIT) + + Copyright (c) 2014 Armon Dadgar + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +notices: [] diff --git a/.licenses/mdns-discovery/go/github.com/miekg/dns.dep.yml b/.licenses/mdns-discovery/go/github.com/miekg/dns.dep.yml index 4a388fc..2105f37 100644 --- a/.licenses/mdns-discovery/go/github.com/miekg/dns.dep.yml +++ b/.licenses/mdns-discovery/go/github.com/miekg/dns.dep.yml @@ -1,6 +1,6 @@ --- name: github.com/miekg/dns -version: v1.1.31 +version: v1.1.41 type: go summary: Package dns implements a full featured interface to the Domain Name System. homepage: https://pkg.go.dev/github.com/miekg/dns diff --git a/.licenses/mdns-discovery/go/golang.org/x/crypto/ed25519.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/crypto/ed25519.dep.yml deleted file mode 100644 index 6949ccc..0000000 --- a/.licenses/mdns-discovery/go/golang.org/x/crypto/ed25519.dep.yml +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: golang.org/x/crypto/ed25519 -version: v0.0.0-20201002094018-c90954cbb977 -type: go -summary: Package ed25519 implements the Ed25519 signature algorithm. -homepage: https://pkg.go.dev/golang.org/x/crypto/ed25519 -license: bsd-3-clause -licenses: -- sources: crypto@v0.0.0-20201002094018-c90954cbb977/LICENSE - text: | - Copyright (c) 2009 The Go Authors. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: crypto@v0.0.0-20201002094018-c90954cbb977/PATENTS - text: | - Additional IP Rights Grant (Patents) - - "This implementation" means the copyrightable works distributed by - Google as part of the Go project. - - Google hereby grants to You a perpetual, worldwide, non-exclusive, - no-charge, royalty-free, irrevocable (except as stated in this section) - patent license to make, have made, use, offer to sell, sell, import, - transfer and otherwise run, modify and propagate the contents of this - implementation of Go, where such license applies only to those patent - claims, both currently owned or controlled by Google and acquired in - the future, licensable by Google that are necessarily infringed by this - implementation of Go. This grant does not include claims that would be - infringed only as a consequence of further modification of this - implementation. If you or your agent or exclusive licensee institute or - order or agree to the institution of patent litigation against any - entity (including a cross-claim or counterclaim in a lawsuit) alleging - that this implementation of Go or any code incorporated within this - implementation of Go constitutes direct or contributory patent - infringement, or inducement of patent infringement, then any patent - rights granted to you under this License for this implementation of Go - shall terminate as of the date such litigation is filed. -notices: [] diff --git a/.licenses/mdns-discovery/go/golang.org/x/net/bpf.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/net/bpf.dep.yml index 31ada49..9f4da01 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/net/bpf.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/net/bpf.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/net/bpf -version: v0.0.0-20200930145003-4acb6c075d10 +version: v0.0.0-20210410081132-afb366fc7cd1 type: go summary: Package bpf implements marshaling and unmarshaling of programs for the Berkeley Packet Filter virtual machine, and provides a Go implementation of the virtual machine. homepage: https://pkg.go.dev/golang.org/x/net/bpf license: bsd-3-clause licenses: -- sources: net@v0.0.0-20200930145003-4acb6c075d10/LICENSE +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: net@v0.0.0-20200930145003-4acb6c075d10/PATENTS +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/net/internal/iana.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/net/internal/iana.dep.yml index ae13051..2b2966d 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/net/internal/iana.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/net/internal/iana.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/net/internal/iana -version: v0.0.0-20200930145003-4acb6c075d10 +version: v0.0.0-20210410081132-afb366fc7cd1 type: go summary: Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA). homepage: https://pkg.go.dev/golang.org/x/net/internal/iana license: bsd-3-clause licenses: -- sources: net@v0.0.0-20200930145003-4acb6c075d10/LICENSE +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: net@v0.0.0-20200930145003-4acb6c075d10/PATENTS +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/net/internal/socket.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/net/internal/socket.dep.yml index 1d3cc4b..968fd7c 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/net/internal/socket.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/net/internal/socket.dep.yml @@ -1,12 +1,12 @@ --- name: golang.org/x/net/internal/socket -version: v0.0.0-20200930145003-4acb6c075d10 +version: v0.0.0-20210410081132-afb366fc7cd1 type: go summary: Package socket provides a portable interface for socket system calls. homepage: https://pkg.go.dev/golang.org/x/net/internal/socket license: bsd-3-clause licenses: -- sources: net@v0.0.0-20200930145003-4acb6c075d10/LICENSE +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: net@v0.0.0-20200930145003-4acb6c075d10/PATENTS +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/net/ipv4.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/net/ipv4.dep.yml index 47d1214..3cd5078 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/net/ipv4.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/net/ipv4.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/net/ipv4 -version: v0.0.0-20200930145003-4acb6c075d10 +version: v0.0.0-20210410081132-afb366fc7cd1 type: go summary: Package ipv4 implements IP-level socket options for the Internet Protocol version 4. homepage: https://pkg.go.dev/golang.org/x/net/ipv4 license: bsd-3-clause licenses: -- sources: net@v0.0.0-20200930145003-4acb6c075d10/LICENSE +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: net@v0.0.0-20200930145003-4acb6c075d10/PATENTS +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/net/ipv6.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/net/ipv6.dep.yml index 58a4533..4be8ccc 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/net/ipv6.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/net/ipv6.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/net/ipv6 -version: v0.0.0-20200930145003-4acb6c075d10 +version: v0.0.0-20210410081132-afb366fc7cd1 type: go summary: Package ipv6 implements IP-level socket options for the Internet Protocol version 6. homepage: https://pkg.go.dev/golang.org/x/net/ipv6 license: bsd-3-clause licenses: -- sources: net@v0.0.0-20200930145003-4acb6c075d10/LICENSE +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: net@v0.0.0-20200930145003-4acb6c075d10/PATENTS +- sources: net@v0.0.0-20210410081132-afb366fc7cd1/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/sys/internal/unsafeheader.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/sys/internal/unsafeheader.dep.yml index 345381a..825b43a 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/sys/internal/unsafeheader.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/sys/internal/unsafeheader.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/sys/internal/unsafeheader -version: v0.0.0-20200930185726-fdedc70b468f +version: v0.0.0-20210330210617-4fbd30eecc44 type: go summary: Package unsafeheader contains header declarations for the Go runtime's slice and string implementations. homepage: https://pkg.go.dev/golang.org/x/sys/internal/unsafeheader license: bsd-3-clause licenses: -- sources: sys@v0.0.0-20200930185726-fdedc70b468f/LICENSE +- sources: sys@v0.0.0-20210330210617-4fbd30eecc44/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: sys@v0.0.0-20200930185726-fdedc70b468f/PATENTS +- sources: sys@v0.0.0-20210330210617-4fbd30eecc44/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/mdns-discovery/go/golang.org/x/sys/unix.dep.yml b/.licenses/mdns-discovery/go/golang.org/x/sys/unix.dep.yml index 10a048c..c0a0f77 100644 --- a/.licenses/mdns-discovery/go/golang.org/x/sys/unix.dep.yml +++ b/.licenses/mdns-discovery/go/golang.org/x/sys/unix.dep.yml @@ -1,12 +1,12 @@ --- name: golang.org/x/sys/unix -version: v0.0.0-20200930185726-fdedc70b468f +version: v0.0.0-20210330210617-4fbd30eecc44 type: go summary: Package unix contains an interface to the low-level operating system primitives. homepage: https://pkg.go.dev/golang.org/x/sys/unix license: bsd-3-clause licenses: -- sources: sys@v0.0.0-20200930185726-fdedc70b468f/LICENSE +- sources: sys@v0.0.0-20210330210617-4fbd30eecc44/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: sys@v0.0.0-20200930185726-fdedc70b468f/PATENTS +- sources: sys@v0.0.0-20210330210617-4fbd30eecc44/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..5f45d91 --- /dev/null +++ b/cache.go @@ -0,0 +1,121 @@ +// +// This file is part of mdns-discovery. +// +// Copyright 2018-2021 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +package main + +import ( + "context" + "fmt" + "sync" + "time" + + discovery "github.com/arduino/pluggable-discovery-protocol-handler" +) + +// cacheItem stores TTL of discovered ports and its timer to handle TTL. +type cacheItem struct { + timerTTL *time.Timer + ctx context.Context + cancelFunc context.CancelFunc +} + +// portsCached is a cache to store discovered ports with a TTL. +// Ports that reach their TTL are automatically deleted and +// main discovery process is notified. +// All operations on the internal data are thread safe. +type portsCache struct { + data map[string]*cacheItem + dataMutex sync.Mutex + itemsTTL time.Duration + deletionCallback func(port *discovery.Port) +} + +// newCache creates a new portsCache and returns it. +// itemsTTL is the TTL of a single item, when it's reached +// the stored item is deleted. +func newCache(itemsTTL time.Duration, deletionCallback func(port *discovery.Port)) *portsCache { + return &portsCache{ + itemsTTL: itemsTTL, + data: make(map[string]*cacheItem), + deletionCallback: deletionCallback, + } +} + +// storeOrUpdate stores a new port and sets its TTL or +// updates the TTL if already stored. +// Return true if the port TTL has been updated, false otherwise +// storeOrUpdate is thread safe. +func (c *portsCache) storeOrUpdate(port *discovery.Port) bool { + key := fmt.Sprintf("%s:%s %s", port.Address, port.Properties.Get("port"), port.Properties.Get("board")) + c.dataMutex.Lock() + defer c.dataMutex.Unlock() + // We need a cancellable context to avoid leaving + // goroutines hanging if an item's timer TTL is stopped. + ctx, cancelFunc := context.WithCancel(context.Background()) + item, ok := c.data[key] + timerTTL := time.NewTimer(c.itemsTTL) + if ok { + item.timerTTL.Stop() + // If we stop the timer the goroutine that waits + // for it to go off would just hang forever if we + // don't cancel the item's context + item.cancelFunc() + item.timerTTL = timerTTL + item.ctx = ctx + item.cancelFunc = cancelFunc + } else { + item = &cacheItem{ + timerTTL: timerTTL, + ctx: ctx, + cancelFunc: cancelFunc, + } + c.data[key] = item + } + + go func(key string, item *cacheItem, port *discovery.Port) { + select { + case <-item.timerTTL.C: + c.dataMutex.Lock() + defer c.dataMutex.Unlock() + c.deletionCallback(port) + delete(c.data, key) + return + case <-item.ctx.Done(): + // The TTL has been renewed, we also stop the timer + // that keeps track of the TTL. + // The channel of a stopped timer will never fire so + // if keep waiting for it in this goroutine it will + // hang forever. + // Using a context we handle this gracefully. + return + } + }(key, item, port) + + return ok +} + +// clear removes all the stored items and stops their TTL timers. +// clear is thread safe. +func (c *portsCache) clear() { + c.dataMutex.Lock() + defer c.dataMutex.Unlock() + for key, item := range c.data { + item.timerTTL.Stop() + item.cancelFunc() + delete(c.data, key) + } +} diff --git a/go.mod b/go.mod index 2035e02..3153e1d 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,5 @@ require ( github.com/arduino/go-paths-helper v1.6.1 // indirect github.com/arduino/go-properties-orderedmap v1.5.0 github.com/arduino/pluggable-discovery-protocol-handler v1.2.0 - github.com/brutella/dnssd v1.1.1 - github.com/miekg/dns v1.1.31 // indirect - golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 // indirect - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect + github.com/hashicorp/mdns v1.0.4 ) diff --git a/go.sum b/go.sum index 25c8f27..7c0a6cd 100644 --- a/go.sum +++ b/go.sum @@ -6,13 +6,12 @@ github.com/arduino/go-properties-orderedmap v1.5.0 h1:istmr13qQN3nneuU3lsqlMvI6j github.com/arduino/go-properties-orderedmap v1.5.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= github.com/arduino/pluggable-discovery-protocol-handler v1.2.0 h1:gw6W8CtgGc+kh+DKfh+z6cUVPqaZh9Tu3XCt/uGgJUE= github.com/arduino/pluggable-discovery-protocol-handler v1.2.0/go.mod h1:vQfYGJnunfcscLoUcZKqJBlEkZ/qiE28TQj+RV9UT74= -github.com/brutella/dnssd v1.1.1 h1:Ar5ytE2Z9x5DTmuNnASlMTBpcQWQLm9ceHb326s0ykg= -github.com/brutella/dnssd v1.1.1/go.mod h1:9gIcMKQSJvYlO2x+HR50cqqjghb9IWK9hvykmyveVVs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/miekg/dns v1.1.1/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -20,29 +19,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 h1:yH6opeNE+0SY+7pXT4gclZUoKHogXeC2EvOSHGOMGPU= -golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977/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-20181201002055-351d144fa1fc/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-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -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-20181206074257-70b957f3b65e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go index dde3816..0abd9a2 100644 --- a/main.go +++ b/main.go @@ -20,12 +20,16 @@ package main import ( "context" "fmt" + "io/ioutil" + "log" "os" "strconv" + "strings" + "time" properties "github.com/arduino/go-properties-orderedmap" discovery "github.com/arduino/pluggable-discovery-protocol-handler" - "github.com/brutella/dnssd" + "github.com/hashicorp/mdns" ) func main() { @@ -36,18 +40,29 @@ func main() { fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) os.Exit(1) } - } -const mdnsServiceName = "_arduino._tcp.local." +const mdnsServiceName = "_arduino._tcp" + +// Discovered ports stay alive for this amount of time +// since the last time they've been found by an mDNS query. +const portsTTL = time.Second * 60 + +// This is interval at which mDNS queries are made. +const discoveryInterval = time.Second * 15 // MDNSDiscovery is the implementation of the network pluggable-discovery type MDNSDiscovery struct { - cancelFunc func() + cancelFunc func() + entriesChan chan *mdns.ServiceEntry + + portsCache *portsCache } // Hello handles the pluggable-discovery HELLO command func (d *MDNSDiscovery) Hello(userAgent string, protocolVersion int) error { + // The mdns library used has some logs statement that we must disable + log.SetOutput(ioutil.Discard) return nil } @@ -57,52 +72,115 @@ func (d *MDNSDiscovery) Stop() error { d.cancelFunc() d.cancelFunc = nil } + if d.entriesChan != nil { + close(d.entriesChan) + d.entriesChan = nil + } + if d.portsCache != nil { + d.portsCache.clear() + } return nil } // Quit handles the pluggable-discovery QUIT command func (d *MDNSDiscovery) Quit() { + d.Stop() } // StartSync handles the pluggable-discovery START_SYNC command func (d *MDNSDiscovery) StartSync(eventCB discovery.EventCallback, errorCB discovery.ErrorCallback) error { - addFn := func(srv dnssd.Service) { - eventCB("add", newBoardPortJSON(&srv)) + if d.entriesChan != nil { + return fmt.Errorf("already syncing") } - remFn := func(srv dnssd.Service) { - eventCB("remove", newBoardPortJSON(&srv)) + + if d.portsCache == nil { + // Initialize the cache if not already done + d.portsCache = newCache(portsTTL, func(port *discovery.Port) { + eventCB("remove", port) + }) } - ctx, cancel := context.WithCancel(context.Background()) + d.entriesChan = make(chan *mdns.ServiceEntry) go func() { - if err := dnssd.LookupType(ctx, mdnsServiceName, addFn, remFn); err != nil { - errorCB("mdns lookup error: " + err.Error()) + for entry := range d.entriesChan { + port := toDiscoveryPort(entry) + if updated := d.portsCache.storeOrUpdate(port); !updated { + // Port is not cached so let the user know a new one has been found + eventCB("add", port) + } + } + }() + + // We use a separate channel to consume the events received + // from Query and send them over to d.entriesChan only if + // it's open. + // If we'd have used d.entriesChan to get the events from + // Query we risk panics cause of sends to a closed channel. + // Query doesn't stop right away when we call d.Stop() + // neither we have to any to do it, we can only wait for it + // to return. + queriesChan := make(chan *mdns.ServiceEntry) + params := &mdns.QueryParam{ + Service: mdnsServiceName, + Domain: "local", + Timeout: discoveryInterval, + Entries: queriesChan, + WantUnicastResponse: false, + } + + ctx, cancel := context.WithCancel(context.Background()) + go func() { + defer close(queriesChan) + for { + if err := mdns.Query(params); err != nil { + errorCB("mdns lookup error: " + err.Error()) + } + select { + default: + case <-ctx.Done(): + return + } + } + }() + go func() { + for entry := range queriesChan { + if d.entriesChan != nil { + d.entriesChan <- entry + } } }() d.cancelFunc = cancel return nil } -func newBoardPortJSON(port *dnssd.Service) *discovery.Port { - ip := "127.0.0.1" - if len(port.IPs) > 0 { - ip = port.IPs[0].String() +func toDiscoveryPort(entry *mdns.ServiceEntry) *discovery.Port { + ip := "" + if len(entry.AddrV4) > 0 { + ip = entry.AddrV4.String() + } else if len(entry.AddrV6) > 0 { + ip = entry.AddrV6.String() } props := properties.NewMap() - props.Set("ttl", strconv.Itoa(int(port.TTL.Seconds()))) - props.Set("hostname", port.Hostname()) - props.Set("port", strconv.Itoa(port.Port)) - for key, value := range port.Text { + props.Set("hostname", entry.Host) + props.Set("port", strconv.Itoa(entry.Port)) + + for _, field := range entry.InfoFields { + split := strings.Split(field, "=") + if len(split) != 2 { + continue + } + key, value := split[0], split[1] props.Set(key, value) if key == "board" { // duplicate for backwards compatibility props.Set(".", value) } } + return &discovery.Port{ Address: ip, - AddressLabel: port.Name + " at " + ip, + AddressLabel: fmt.Sprintf("%s at %s", entry.Name, ip), Protocol: "network", ProtocolLabel: "Network Port", Properties: props,