Switch mDNS probe to multicast, use mDNS for reachability
Unicast mDNS queries (direct to IP:5353) are ignored by some HomeKit devices. Switch to multicast (224.0.0.251:5353) and filter responses by sender IP. Also consider mDNS response as reachability signal. Split probe timeouts: 100ms for ports/DNS/HTTP, 120ms total to give mDNS extra time. HomeKit responds in ~0.2ms via multicast.
This commit is contained in:
@@ -16,7 +16,7 @@ import (
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
const probeTimeout = 100 * time.Millisecond
|
||||
const probeTimeout = 120 * time.Millisecond
|
||||
|
||||
var log zerolog.Logger
|
||||
var db *sql.DB
|
||||
@@ -78,14 +78,17 @@ func runProbe(parent context.Context, ip string) *probe.Response {
|
||||
}()
|
||||
}
|
||||
|
||||
fastCtx, fastCancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
defer fastCancel()
|
||||
|
||||
run(func() {
|
||||
r, _ := probe.ScanPorts(ctx, ip, ports)
|
||||
r, _ := probe.ScanPorts(fastCtx, ip, ports)
|
||||
mu.Lock()
|
||||
resp.Probes.Ports = r
|
||||
mu.Unlock()
|
||||
})
|
||||
run(func() {
|
||||
r, _ := probe.ReverseDNS(ctx, ip)
|
||||
r, _ := probe.ReverseDNS(fastCtx, ip)
|
||||
mu.Lock()
|
||||
resp.Probes.DNS = r
|
||||
mu.Unlock()
|
||||
@@ -107,7 +110,7 @@ func runProbe(parent context.Context, ip string) *probe.Response {
|
||||
mu.Unlock()
|
||||
})
|
||||
run(func() {
|
||||
r, _ := probe.ProbeHTTP(ctx, ip, nil)
|
||||
r, _ := probe.ProbeHTTP(fastCtx, ip, nil)
|
||||
mu.Lock()
|
||||
resp.Probes.HTTP = r
|
||||
mu.Unlock()
|
||||
@@ -116,7 +119,8 @@ func runProbe(parent context.Context, ip string) *probe.Response {
|
||||
wg.Wait()
|
||||
|
||||
// determine reachable
|
||||
resp.Reachable = resp.Probes.Ports != nil && len(resp.Probes.Ports.Open) > 0
|
||||
resp.Reachable = (resp.Probes.Ports != nil && len(resp.Probes.Ports.Open) > 0) ||
|
||||
resp.Probes.MDNS != nil
|
||||
|
||||
// determine type
|
||||
resp.Type = "standard"
|
||||
|
||||
+25
-15
@@ -22,8 +22,11 @@ const (
|
||||
categoryDoorbell = "18"
|
||||
)
|
||||
|
||||
// QueryHAP sends unicast mDNS query to ip:5353 for HomeKit service.
|
||||
// Returns nil if device is not a HomeKit camera/doorbell.
|
||||
var multicastAddr = &net.UDPAddr{IP: net.IP{224, 0, 0, 251}, Port: 5353}
|
||||
|
||||
// QueryHAP sends multicast mDNS query for HomeKit service and waits
|
||||
// for a response from the specified ip. Returns nil if device is not
|
||||
// a HomeKit camera/doorbell.
|
||||
func QueryHAP(ctx context.Context, ip string) (*MDNSResult, error) {
|
||||
msg := &dns.Msg{
|
||||
Question: []dns.Question{
|
||||
@@ -36,7 +39,7 @@ func QueryHAP(ctx context.Context, ip string) (*MDNSResult, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := net.ListenPacket("udp4", ":0")
|
||||
conn, err := net.ListenMulticastUDP("udp4", nil, multicastAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -44,27 +47,34 @@ func QueryHAP(ctx context.Context, ip string) (*MDNSResult, error) {
|
||||
|
||||
deadline, ok := ctx.Deadline()
|
||||
if !ok {
|
||||
deadline = time.Now().Add(100 * time.Millisecond)
|
||||
deadline = time.Now().Add(time.Second)
|
||||
}
|
||||
_ = conn.SetDeadline(deadline)
|
||||
|
||||
addr := &net.UDPAddr{IP: net.ParseIP(ip), Port: 5353}
|
||||
if _, err = conn.WriteTo(query, addr); err != nil {
|
||||
if _, err = conn.WriteTo(query, multicastAddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetIP := net.ParseIP(ip)
|
||||
buf := make([]byte, 1500)
|
||||
n, _, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return nil, nil // timeout = not a HomeKit device
|
||||
}
|
||||
|
||||
var resp dns.Msg
|
||||
if err = resp.Unpack(buf[:n]); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
for {
|
||||
n, from, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return nil, nil // timeout
|
||||
}
|
||||
|
||||
return parseHAPResponse(&resp)
|
||||
if !from.(*net.UDPAddr).IP.Equal(targetIP) {
|
||||
continue
|
||||
}
|
||||
|
||||
var resp dns.Msg
|
||||
if err = resp.Unpack(buf[:n]); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return parseHAPResponse(&resp)
|
||||
}
|
||||
}
|
||||
|
||||
// internals
|
||||
|
||||
Reference in New Issue
Block a user