@@ -26,6 +26,7 @@ import (
26
26
"os"
27
27
"strconv"
28
28
"strings"
29
+ "sync"
29
30
"time"
30
31
31
32
properties "github.com/arduino/go-properties-orderedmap"
@@ -49,8 +50,11 @@ const mdnsServiceName = "_arduino._tcp"
49
50
// since the last time they've been found by an mDNS query.
50
51
const portsTTL = time .Second * 60
51
52
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
54
58
55
59
// IP address used to check if we're connected to a local network
56
60
var ipv4Addr = & net.UDPAddr {
@@ -64,6 +68,12 @@ var ipv6Addr = &net.UDPAddr{
64
68
Port : 5353 ,
65
69
}
66
70
71
+ // QueryParam{} has to select which IP version(s) to use
72
+ type connectivity struct {
73
+ IPv4 bool
74
+ IPv6 bool
75
+ }
76
+
67
77
// MDNSDiscovery is the implementation of the network pluggable-discovery
68
78
type MDNSDiscovery struct {
69
79
cancelFunc func ()
@@ -140,55 +150,131 @@ func (d *MDNSDiscovery) StartSync(eventCB discovery.EventCallback, errorCB disco
140
150
ctx , cancel := context .WithCancel (context .Background ())
141
151
go func () {
142
152
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
+ }
143
165
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 ()
147
173
if err != nil {
148
- disableIPv6 = true
149
- } else {
150
- mconn6 .Close ()
174
+ goto NEXT
151
175
}
152
176
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 ():
157
198
return
158
199
}
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 {
162
231
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
+ }
163
242
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
171
247
}
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
181
251
}
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
188
255
}
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
+ }
192
278
}
193
279
194
280
func toDiscoveryPort (entry * mdns.ServiceEntry ) * discovery.Port {
0 commit comments