@@ -26,6 +26,7 @@ import (
2626 "os"
2727 "strconv"
2828 "strings"
29+ "sync"
2930 "time"
3031
3132 properties "github.com/arduino/go-properties-orderedmap"
@@ -49,8 +50,11 @@ const mdnsServiceName = "_arduino._tcp"
4950// since the last time they've been found by an mDNS query.
5051const portsTTL = time .Second * 60
5152
52- // This is interval at which mDNS queries are made.
53- const discoveryInterval = time .Second * 15
53+ // Interval at which we check available network interfaces and call mdns.Query()
54+ const queryInterval = time .Second * 30
55+
56+ // mdns.Query() will either exit early or timeout after this amount of time
57+ const queryTimeout = time .Second * 15
5458
5559// IP address used to check if we're connected to a local network
5660var ipv4Addr = & net.UDPAddr {
@@ -64,6 +68,12 @@ var ipv6Addr = &net.UDPAddr{
6468 Port : 5353 ,
6569}
6670
71+ // QueryParam{} has to select which IP version(s) to use
72+ type connectivity struct {
73+ IPv4 bool
74+ IPv6 bool
75+ }
76+
6777// MDNSDiscovery is the implementation of the network pluggable-discovery
6878type MDNSDiscovery struct {
6979 cancelFunc func ()
@@ -140,55 +150,131 @@ func (d *MDNSDiscovery) StartSync(eventCB discovery.EventCallback, errorCB disco
140150 ctx , cancel := context .WithCancel (context .Background ())
141151 go func () {
142152 defer close (queriesChan )
153+ queryLoop (ctx , queriesChan )
154+ }()
155+ go func () {
156+ for entry := range queriesChan {
157+ if d .entriesChan != nil {
158+ d .entriesChan <- entry
159+ }
160+ }
161+ }()
162+ d .cancelFunc = cancel
163+ return nil
164+ }
143165
144- disableIPv6 := false
145- // Check if the current network supports IPv6
146- mconn6 , err := net .ListenMulticastUDP ("udp6" , nil , ipv6Addr )
166+ func queryLoop (ctx context.Context , queriesChan chan <- * mdns.ServiceEntry ) {
167+ for {
168+ var interfaces []net.Interface
169+ var conn connectivity
170+ var wg sync.WaitGroup
171+
172+ interfaces , err := availableInterfaces ()
147173 if err != nil {
148- disableIPv6 = true
149- } else {
150- mconn6 .Close ()
174+ goto NEXT
151175 }
152176
153- // We must check if we're connected to a local network, if we don't
154- // the subsequent mDNS query would fail and return an error.
155- mconn4 , err := net .ListenMulticastUDP ("udp4" , nil , ipv4Addr )
156- if err != nil {
177+ conn = checkConnectivity ()
178+ if ! conn .available () {
179+ goto NEXT
180+ }
181+
182+ wg .Add (len (interfaces ))
183+
184+ for n := range interfaces {
185+ params := makeQueryParams (& interfaces [n ], conn , queriesChan )
186+ go func () {
187+ defer wg .Done ()
188+ mdns .Query (params )
189+ }()
190+ }
191+
192+ wg .Wait ()
193+
194+ NEXT:
195+ select {
196+ case <- time .After (queryInterval ):
197+ case <- ctx .Done ():
157198 return
158199 }
159- // If we managed to open a connection close it, mdns.Query opens
160- // another one on the same IP address we use and it would fail
161- // if we leave this open.
200+ }
201+ }
202+
203+ func (conn * connectivity ) available () bool {
204+ return conn .IPv4 || conn .IPv6
205+ }
206+
207+ func checkConnectivity () connectivity {
208+ // We must check if we're connected to a local network, if we don't
209+ // the subsequent mDNS query would fail and return an error.
210+ // If we managed to open a connection close it, mdns.Query opens
211+ // another one on the same IP address we use and it would fail
212+ // if we leave this open.
213+ out := connectivity {
214+ IPv4 : true ,
215+ IPv6 : true ,
216+ }
217+
218+ // Check if the current network supports IPv6
219+ mconn6 , err := net .ListenMulticastUDP ("udp6" , nil , ipv6Addr )
220+ if err != nil {
221+ out .IPv6 = false
222+ } else {
223+ mconn6 .Close ()
224+ }
225+
226+ // And the same for IPv4
227+ mconn4 , err := net .ListenMulticastUDP ("udp4" , nil , ipv4Addr )
228+ if err != nil {
229+ out .IPv4 = false
230+ } else {
162231 mconn4 .Close ()
232+ }
233+
234+ return out
235+ }
236+
237+ func availableInterfaces () ([]net.Interface , error ) {
238+ interfaces , err := net .Interfaces ()
239+ if err != nil {
240+ return nil , err
241+ }
163242
164- params := & mdns.QueryParam {
165- Service : mdnsServiceName ,
166- Domain : "local" ,
167- Timeout : discoveryInterval ,
168- Entries : queriesChan ,
169- WantUnicastResponse : false ,
170- DisableIPv6 : disableIPv6 ,
243+ var out []net.Interface
244+ for _ , netif := range interfaces {
245+ if netif .Flags & net .FlagUp == 0 {
246+ continue
171247 }
172- for {
173- if err := mdns .Query (params ); err != nil {
174- errorCB ("mdns lookup error: " + err .Error ())
175- }
176- select {
177- default :
178- case <- ctx .Done ():
179- return
180- }
248+
249+ if netif .Flags & net .FlagMulticast == 0 {
250+ continue
181251 }
182- }()
183- go func () {
184- for entry := range queriesChan {
185- if d .entriesChan != nil {
186- d .entriesChan <- entry
187- }
252+
253+ if netif .HardwareAddr == nil {
254+ continue
188255 }
189- }()
190- d .cancelFunc = cancel
191- return nil
256+
257+ out = append (out , netif )
258+ }
259+
260+ if len (out ) == 0 {
261+ return nil , fmt .Errorf ("no valid network interfaces" )
262+ }
263+
264+ return out , nil
265+ }
266+
267+ func makeQueryParams (netif * net.Interface , conn connectivity , queriesChan chan <- * mdns.ServiceEntry ) (params * mdns.QueryParam ) {
268+ return & mdns.QueryParam {
269+ Service : mdnsServiceName ,
270+ Domain : "local" ,
271+ Timeout : queryTimeout ,
272+ Interface : netif ,
273+ Entries : queriesChan ,
274+ WantUnicastResponse : false ,
275+ DisableIPv4 : ! conn .IPv4 ,
276+ DisableIPv6 : ! conn .IPv6 ,
277+ }
192278}
193279
194280func toDiscoveryPort (entry * mdns.ServiceEntry ) * discovery.Port {
0 commit comments