Skip to content

Commit

Permalink
Fix service browsing
Browse files Browse the repository at this point in the history
  • Loading branch information
brutella committed Jul 24, 2020
1 parent 449d64f commit e45ff4e
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 39 deletions.
93 changes: 79 additions & 14 deletions browse.go
@@ -1,24 +1,43 @@
package dnssd

import (
"context"
"github.com/brutella/dnssd/log"
"github.com/miekg/dns"

"context"
"fmt"
"net"
_ "time"
)

type AddServiceFunc func(Service)
type RmvServiceFunc func(Service)
type BrowseEntry struct {
IPs []net.IP
IfaceName string
Name string
Type string
Domain string
}

func LookupType(ctx context.Context, service string, add AddServiceFunc, rmv RmvServiceFunc) (err error) {
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
}
// receive message from same host
// conn.enableMulticastLoopback(true)
defer conn.close()

return lookupType(ctx, service, conn, add, rmv)
}

func lookupType(ctx context.Context, service string, conn MDNSConn, add AddServiceFunc, rmv RmvServiceFunc) (err error) {
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)
Expand All @@ -34,25 +53,71 @@ func lookupType(ctx context.Context, service string, conn MDNSConn, add AddServi

ch := conn.Read(readCtx)

q := &Query{msg: m}
conn.SendQuery(q)
qs := make(chan *Query)
go func() {
for _, iface := range multicastInterfaces() {
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.iface.Name, q.msg)
if err := conn.SendQuery(q); err != nil {
log.Debug.Println("SendQuery:", err)
}

case req := <-ch:
adds, rmvs := cache.UpdateFrom(req.msg)
log.Debug.Printf("Receive message at %s\n%s\n", req.iface.Name, req.msg)
cache.UpdateFrom(req.msg, req.iface)
for _, srv := range cache.Services() {
if srv.ServiceName() != service {
continue
}

for _, srv := range adds {
if srv.ServiceName() == service {
add(*srv)
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,
IfaceName: ifaceName,
Name: srv.Name,
Type: srv.Type,
Domain: srv.Domain,
}
es = append(es, &e)
add(e)
}
}
}

for _, srv := range rmvs {
if srv.ServiceName() == service {
rmv(*srv)
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()
}
Expand Down
28 changes: 12 additions & 16 deletions cache.go
Expand Up @@ -18,13 +18,20 @@ func NewCache() *Cache {
}
}

func (c *Cache) Services() []*Service {
tmp := []*Service{}
for _, s := range c.services {
tmp = append(tmp, s)
}
return tmp
}

// UpdateFrom updates the cache from resource records in msg.
// TODO consider the cache-flush bit to make records as to be deleted in one second
func (c *Cache) UpdateFrom(msg *dns.Msg) (adds []*Service, rmvs []*Service) {
answers := allRecords(msg)
func (c *Cache) UpdateFrom(msg *dns.Msg, iface *net.Interface) (adds []*Service, rmvs []*Service) {
answers := filterRecords(msg, nil)
sort.Sort(byType(answers))

loop:
for _, answer := range answers {
switch rr := answer.(type) {
case *dns.PTR:
Expand Down Expand Up @@ -69,25 +76,14 @@ loop:
case *dns.A:
for _, entry := range c.services {
if entry.Hostname() == rr.Hdr.Name {
for _, ip := range entry.IPs {
if ip.Equal(rr.A) {
continue loop
}
}
entry.IPs = append(entry.IPs, rr.A)
entry.addIP(rr.A, iface)
}
}

case *dns.AAAA:
for _, entry := range c.services {
if entry.Hostname() == rr.Hdr.Name {
for _, ip := range entry.IPs {
if ip.Equal(rr.AAAA) {
continue loop
}
}

entry.IPs = append(entry.IPs, rr.AAAA)
entry.addIP(rr.AAAA, iface)
}
}

Expand Down
17 changes: 11 additions & 6 deletions cmd/browse/main.go
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"github.com/brutella/dnssd"
"github.com/brutella/dnssd/log"
"os"
"os/signal"
"strings"
Expand All @@ -14,12 +15,16 @@ import (

var serviceFlag = flag.String("Type", "_asdf._tcp", "Service type")
var domainFlag = flag.String("Domain", "local.", "Browsing domain")

var verboseFlag = flag.Bool("Verbose", false, "Verbose logging")
var timeFormat = "15:04:05.000"

func main() {
flag.Parse()

if *verboseFlag {
log.Debug.Enable()
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

Expand All @@ -28,14 +33,14 @@ func main() {
fmt.Printf("Browsing for %s\n", service)
fmt.Printf("DATE: –––%s–––\n", time.Now().Format("Mon Jan 2 2006"))
fmt.Printf("%s ...STARTING...\n", time.Now().Format(timeFormat))
fmt.Printf("Timestamp A/R Domain Service Type Service Name\n")
fmt.Printf("Timestamp A/R if Domain Service Type Service Name\n")

addFn := func(srv dnssd.Service) {
fmt.Printf("%s Add %s %s %s\n", time.Now().Format(timeFormat), srv.Domain, srv.Type, srv.Name)
addFn := func(e dnssd.BrowseEntry) {
fmt.Printf("%s Add %s %s %s %s (%s)\n", time.Now().Format(timeFormat), e.IfaceName, e.Domain, e.Type, e.Name, e.IPs)
}

rmvFn := func(srv dnssd.Service) {
fmt.Printf("%s Rmv %s %s %s\n", time.Now().Format(timeFormat), srv.Domain, srv.Type, srv.Name)
rmvFn := func(e dnssd.BrowseEntry) {
fmt.Printf("%s Rmv %s %s %s %s\n", time.Now().Format(timeFormat), e.IfaceName, e.Domain, e.Type, e.Name)
}

if err := dnssd.LookupType(ctx, service, addFn, rmvFn); err != nil {
Expand Down
17 changes: 15 additions & 2 deletions cmd/register/main.go
Expand Up @@ -2,10 +2,12 @@
package main

import (
"github.com/brutella/dnssd"
"github.com/brutella/dnssd/log"

"context"
"flag"
"fmt"
"github.com/brutella/dnssd"
slog "log"
"os"
"os/signal"
Expand All @@ -17,7 +19,8 @@ var instanceFlag = flag.String("Name", "Service", "Service name")
var serviceFlag = flag.String("Type", "_asdf._tcp", "Service type")
var domainFlag = flag.String("Domain", "local", "domain")
var portFlag = flag.Int("Port", 12345, "Port")

var verboseFlag = flag.Bool("Verbose", false, "Verbose logging")
var interfaceFlag = flag.String("Interface", "", "Network interface name")
var timeFormat = "15:04:05.000"

func main() {
Expand All @@ -27,6 +30,10 @@ func main() {
return
}

if *verboseFlag {
log.Debug.Enable()
}

instance := fmt.Sprintf("%s.%s.%s.", strings.Trim(*instanceFlag, "."), strings.Trim(*serviceFlag, "."), strings.Trim(*domainFlag, "."))

fmt.Printf("Registering Service %s port %d\n", instance, *portFlag)
Expand All @@ -39,11 +46,17 @@ func main() {
if resp, err := dnssd.NewResponder(); err != nil {
fmt.Println(err)
} else {
ifaces := []string{}
if len(*interfaceFlag) > 0 {
ifaces = append(ifaces, *interfaceFlag)
}

cfg := dnssd.Config{
Name: *instanceFlag,
Type: *serviceFlag,
Domain: *domainFlag,
Port: *portFlag,
Ifaces: ifaces,
}
srv, err := dnssd.NewService(cfg)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion probe.go
Expand Up @@ -164,7 +164,7 @@ func probeAtInterface(ctx context.Context, conn MDNSConn, service Service, iface
select {
case req := <-ch:
if req.iface.Name != iface.Name {
log.Info.Println("Ignore msg from", req.iface.Name)
log.Debug.Println("Ignore msg from", req.iface.Name)
break
}

Expand Down
4 changes: 4 additions & 0 deletions service.go
Expand Up @@ -299,6 +299,10 @@ func multicastInterfaces() []net.Interface {
continue
}

if (iface.Flags & net.FlagLoopback) != 0 {
continue
}

// check for a valid ip at that interface
addrs, err := iface.Addrs()
if err != nil {
Expand Down

0 comments on commit e45ff4e

Please sign in to comment.