forked from brutella/dnssd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
browse.go
130 lines (113 loc) · 2.73 KB
/
browse.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package dnssd
import (
"github.com/brutella/dnssd/log"
"github.com/miekg/dns"
"context"
"fmt"
"net"
_ "time"
)
type BrowseEntry struct {
IPs []net.IP
Host string
Port int
IfaceName string
Name string
Type string
Domain string
Text map[string]string
}
type AddFunc func(BrowseEntry)
type RmvFunc func(BrowseEntry)
func LookupType(ctx context.Context, service string, add AddFunc, rmv RmvFunc) (err error) {
conn, err := newMDNSConn()
if err != nil {
return err
}
defer conn.close()
return lookupType(ctx, service, conn, add, rmv)
}
func (e BrowseEntry) ServiceInstanceName() string {
return fmt.Sprintf("%s.%s.%s.", e.Name, e.Type, e.Domain)
}
func lookupType(ctx context.Context, service string, conn MDNSConn, add AddFunc, rmv RmvFunc) (err error) {
var cache = NewCache()
m := new(dns.Msg)
m.Question = []dns.Question{
dns.Question{service, dns.TypePTR, dns.ClassINET},
}
// TODO include known answers which current ttl is more than half of the correct ttl (see TFC6772 7.1: Known-Answer Supression)
// m.Answer = ...
// m.Authoritive = false // because our answers are *believes*
readCtx, readCancel := context.WithCancel(ctx)
defer readCancel()
ch := conn.Read(readCtx)
qs := make(chan *Query)
go func() {
for _, iface := range multicastInterfaces() {
iface := iface
q := &Query{msg: m, iface: iface}
qs <- q
}
}()
es := []*BrowseEntry{}
for {
select {
case q := <-qs:
log.Debug.Printf("Send browsing query at %s\n%s\n", q.IfaceName(), q.msg)
if err := conn.SendQuery(q); err != nil {
log.Debug.Println("SendQuery:", err)
}
case req := <-ch:
log.Debug.Printf("Receive message at %s\n%s\n", req.IfaceName(), req.msg)
cache.UpdateFrom(req.msg, req.iface)
for _, srv := range cache.Services() {
if srv.ServiceName() != service {
continue
}
for ifaceName, ips := range srv.ifaceIPs {
var found = false
for _, e := range es {
if e.Name == srv.Name && e.IfaceName == ifaceName {
found = true
break
}
}
if !found {
e := BrowseEntry{
IPs: ips,
Host: srv.Host,
Port: srv.Port,
IfaceName: ifaceName,
Name: srv.Name,
Type: srv.Type,
Domain: srv.Domain,
Text: srv.Text,
}
es = append(es, &e)
add(e)
}
}
}
tmp := []*BrowseEntry{}
for _, e := range es {
var found = false
for _, srv := range cache.Services() {
if srv.ServiceInstanceName() == e.ServiceInstanceName() {
found = true
break
}
}
if found {
tmp = append(tmp, e)
} else {
// TODO
rmv(*e)
}
}
es = tmp
case <-ctx.Done():
return ctx.Err()
}
}
}