func (s *MDNS) ServiceMemberDiscovery(service string) []string {
	dn := serviceFQDN(service)
	memberMap := make(map[string]struct{}, 0)
	req := lookupRequest{dn, dns.TypePTR, make(chan dns.RR, 10)}
	s.lookup <- req
	for rr := <-req.rc; rr != nil; rr = <-req.rc {
		switch rr := rr.(type) {
		case *dns.RR_PTR:
			memberMap[rr.Ptr] = struct{}{}
		}
	}
	var reply []string
	for member := range memberMap {
		reply = append(reply, member)
	}
	return reply
}
ServiceMemberDiscovery returns all the members of a service (i.e. with a PTR record).
package mdns
import (
	"errors"
	"fmt"
	"log"
	"net"
	"reflect"
	"strings"
	"sync"
	"time"
	"github.com/presotto/go-mdns-sd/go_dns"
)
type msgFromNet struct {
	mifc   *multicastIfc  
	sender *net.UDPAddr   
	msg    *dns.Msg
}
type multicastIfc struct {
	ifc net.Interface
	addr *net.UDPAddr
	ipver int
	addresses []*net.IPNet
	conn *net.UDPConn
	cache *rrCache
	mdns *MDNS
	doneLock sync.Mutex
	done     bool
}
func newMulticastIfc(ipver int, ifc net.Interface, addr *net.UDPAddr, addresses []*net.IPNet, mdns *MDNS) *multicastIfc {
	return &multicastIfc{
		ifc:       ifc,
		addr:      addr,
		addresses: addresses,
		cache:     newRRCache(mdns.logLevel),
		mdns:      mdns,
		ipver:     ipver,
	}
}
func (m *multicastIfc) run() bool {
	m.doneLock.Lock()
	defer m.doneLock.Unlock()
	return !m.done
}
func (m *multicastIfc) stop() {
	m.doneLock.Lock()
	m.done = true
	m.doneLock.Unlock()
}
func (m *multicastIfc) String() string {
	return fmt.Sprintf("%d v%d %s multicast addr %s", m.ifc.Index, m.ipver, m.ifc.Name, m.addr)
}
func (m *multicastIfc) appendHostAddresses(msg *dns.Msg, host string, rrtype int, ttl uint32) {
	hostDN := hostFQDN(host)
	for _, address := range m.addresses {
		switch rrtype {
		case dns.TypeALL:
			msg.Answer = append(msg.Answer, NewAddressRR(hostDN, 0x8000|dns.ClassINET, ttl, address.IP))
		case dns.TypeA:
			if v4 := address.IP.To4(); v4 != nil {
				msg.Answer = append(msg.Answer, NewAddressRR(hostDN, 0x8000|dns.ClassINET, ttl, v4))
			}
		case dns.TypeAAAA:
			if v4 := address.IP.To4(); v4 == nil {
				msg.Answer = append(msg.Answer, NewAddressRR(hostDN, 0x8000|dns.ClassINET, ttl, address.IP))
			}
		}
	}
}
func (m *multicastIfc) appendSrvRR(msg *dns.Msg, service, host string, port uint16, ttl uint32) {
	hostDN := hostFQDN(host)
	uniqueServiceDN := instanceFQDN(host, service)
	msg.Answer = append(msg.Answer, NewSrvRR(uniqueServiceDN, 0x8000|dns.ClassINET, ttl, hostDN, port, 0, 0))
}
func (m *multicastIfc) appendTxtRR(msg *dns.Msg, service, host string, txt []string, ttl uint32) {
	uniqueServiceDN := instanceFQDN(host, service)
	msg.Answer = append(msg.Answer, NewTxtRR(uniqueServiceDN, 0x8000|dns.ClassINET, ttl, txt))
}
func (m *multicastIfc) appendDiscoveryRecords(msg *dns.Msg, service, host string, port uint16, txt []string, ttl uint32) {
	serviceDN := serviceFQDN(service)
	uniqueServiceDN := instanceFQDN(host, service)
	msg.Answer = append(msg.Answer, NewPtrRR(serviceDN, dns.ClassINET, ttl, uniqueServiceDN))
	m.appendTxtRR(msg, service, host, txt, ttl)
	m.appendSrvRR(msg, service, host, port, ttl)
	if port > 0 {
		m.appendHostAddresses(msg, host, dns.TypeALL, ttl)
	}
}
func (m *multicastIfc) sendMessage(msg *dns.Msg) {
	if m.mdns.logLevel >= 2 {
		log.Printf("sending message %v\n", msg)
	}
	buf, ok := msg.Pack()
	if !ok {
		if m.mdns.logLevel >= 1 {
			log.Printf("can't pack address message\n")
		}
		return
	}
	if _, err := m.conn.WriteTo(buf, m.addr); err != nil {
		if m.mdns.logLevel >= 1 {
			log.Printf("WriteTo failed %v %v", m.addr, err)
		}
	}
	for _, rr := range msg.Answer {
		if m.cache.Add(rr) {
			m.mdns.changedRR(rr)
		}
	}
}
func (m *multicastIfc) announceHost(host string, ttl uint32) {
	msg := newDnsMsg(0, true, true)
	m.appendHostAddresses(msg, host, dns.TypeALL, ttl)
	m.sendMessage(msg)
}
func (m *multicastIfc) announceService(service, host string, port uint16, txt []string, ttl uint32) {
	msg := newDnsMsg(0, true, true)
	m.appendDiscoveryRecords(msg, service, host, port, txt, ttl)
	m.sendMessage(msg)
}
func (m *multicastIfc) sendQuestion(q []dns.Question) {
	msg := newDnsMsg(0, false, false)
	msg.Question = q
	m.sendMessage(msg)
}
type lookupRequest struct {
	name   string
	rrtype uint16
	rc     chan dns.RR
}
type announceRequest struct {
	service string
	host    string
	port    uint16
	txt     []string
}
type updateRequest struct {
	done chan struct{}
	host string
	ttl  uint32
}
type watchedService struct {
	c    *sync.Cond
	gen  int
	done bool
}
type MDNS struct {
	v4addr, v6addr *net.UDPAddr
	mifcsLock sync.RWMutex
	mifcs     map[string]*multicastIfc
	doneLock sync.Mutex
	done     bool
	fromNet chan *msgFromNet
	announce chan announceRequest
	goodbye  chan announceRequest
	lookup   chan lookupRequest
	update   chan updateRequest
	refreshAlarm *time.Ticker
	cleanupAlarm *time.Ticker
	hostName string
	hostFQDN string
	services map[string]map[string]announceRequest
	watchedLock sync.RWMutex
	watched     map[string][]*watchedService
	subscribed  map[string]bool
	ttl uint32
	logLevel int
	loopback bool
}
func losecolons(x string) string {
	var y string
	for _, c := range x {
		if c != ':' {
			y = y + string(c)
		}
	}
	return y
}
func ipsAreAllMine(ips []net.IP) bool {
	if len(ips) == 0 {
		return true
	}
	addrs, _ := net.InterfaceAddrs()
	for _, ip := range ips {
		found := false
		for _, addr := range addrs {
			switch x := addr.(type) {
			case *net.IPNet:
				if x.IP.Equal(ip) {
					found = true
					break
				}
			}
		}
		if !found {
			return false
		}
	}
	return true
}
func (s *MDNS) isDoppelGanger(rr []dns.RR) bool {
	var ips []net.IP
	for _, rr := range rr {
		if rr.Header().Name != s.hostFQDN {
			continue
		}
		switch x := rr.(type) {
		case *dns.RR_A:
			ips = append(ips, AtoIP(x))
		case *dns.RR_AAAA:
			ips = append(ips, AAAAtoIP(x))
		}
	}
	if len(ips) == 0 {
		return false
	}
	return !ipsAreAllMine(ips)
}
func NewMDNS(host, v4addr, v6addr string, loopback bool, logLevel int) (s *MDNS, err error) {
	s = new(MDNS)
	if v4addr == "" {
		v4addr = "224.0.0.251:5353"
	}
	if v6addr == "" {
		v6addr = "[FF02::FB]:5353"
	}
	if s.v4addr, err = net.ResolveUDPAddr("udp", v4addr); err != nil {
		return nil, err
	}
	if s.v6addr, err = net.ResolveUDPAddr("udp", v6addr); err != nil {
		return nil, err
	}
	s.logLevel = logLevel
	s.loopback = loopback
	s.ttl = 120
	s.fromNet = make(chan *msgFromNet, 10)
	s.announce = make(chan announceRequest)
	s.goodbye = make(chan announceRequest)
	s.lookup = make(chan lookupRequest)
	s.update = make(chan updateRequest)
	s.services = make(map[string]map[string]announceRequest, 0)
	s.watched = make(map[string][]*watchedService, 0)
	s.subscribed = make(map[string]bool, 0)
	s.mifcs = make(map[string]*multicastIfc, 0)
	highesthwaddr, err := s.ScanInterfaces()
	if err != nil {
		log.Fatalf("scanning interfaces: %s", err)
	}
	s.setAlarms()
	go s.mainLoop()
	if len(host) != 0 {
		if strings.HasSuffix(host, "()") {
			host = fmt.Sprintf("%s(%s)", strings.TrimSuffix(host, "()"), losecolons(highesthwaddr))
		}
		ips, _ := s.ResolveAddress(host)
		if !ipsAreAllMine(ips) {
			s.Stop()
			return nil, errors.New("host name in use")
		}
		req := updateRequest{done: make(chan struct{}), host: host}
		s.update <- req
		<-req.done
	}
	return s, nil
}
func equalAddresses(al, bl []*net.IPNet) bool {
	if len(al) != len(bl) {
		return false
	}
L:
	for _, a := range al {
		for _, b := range bl {
			if a.IP.Equal(b.IP) {
				continue L
			}
		}
		return false
	}
	return true
}
func (s *MDNS) ScanInterfaces() (string, error) {
	highesthwaddr := ""
	ifcs, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	newmifcs := make(map[string]*multicastIfc, 0)
	for _, ifc := range ifcs {
		addresses, addrErr := ifc.Addrs()
		if addrErr != nil {
			if s.logLevel >= 1 {
				log.Printf("Addrs() failed: %s", addrErr)
			}
			continue
		}
		hasv4 := false
		hasv6 := false
		var okAddresses []*net.IPNet
		hwaddr := ifc.HardwareAddr.String()
		if hwaddr > highesthwaddr {
			highesthwaddr = hwaddr
		}
		key := fmt.Sprintf("%s+%d+%s", ifc.Name, ifc.Index, hwaddr)
		for _, address := range addresses {
			switch address := address.(type) {
			case *net.IPNet:
				if (address.IP.IsLoopback() && !s.loopback) || (!address.IP.IsLoopback() && s.loopback) {
					if s.logLevel >= 1 {
						log.Printf("skipping ifc %d %s %s\n", ifc.Index, ifc.Name, address)
					}
					continue
				}
				if address.IP.To4() != nil {
					hasv4 = true
				} else {
					hasv6 = true
				}
				okAddresses = append(okAddresses, address)
			}
		}
		if okAddresses == nil {
			continue
		}
		if hasv4 {
			newmifcs["4+"+key] = newMulticastIfc(4, ifc, s.v4addr, okAddresses, s)
		}
		if hasv6 {
			newmifcs["6+"+key] = newMulticastIfc(6, ifc, s.v6addr, okAddresses, s)
		}
	}
	s.mifcsLock.Lock()
	defer s.mifcsLock.Unlock()
	for k, m := range s.mifcs {
		if newm, ok := newmifcs[k]; ok {
			if equalAddresses(newm.addresses, m.addresses) {
				continue
			}
		}
		m.stop()
		if s.logLevel >= 1 {
			log.Printf("removing ifc %s", m)
		}
		delete(s.mifcs, k)
	}
	for k, newm := range newmifcs {
		if _, ok := s.mifcs[k]; ok {
			continue
		}
		conn, err := net.ListenMulticastUDP("udp", &newm.ifc, newm.addr)
		if err != nil {
			if s.logLevel >= 1 {
				log.Printf("ListenMulticastUDP %s: %v\n", newm, err)
			}
			continue
		}
		if err := SetMulticastTTL(conn, newm.ipver, 255); err != nil {
			if s.logLevel >= 1 {
				log.Printf("SetMulticastTTL %s: %v\n", newm, err)
			}
		}
		if err := SetMulticastLoopback(conn, newm.ipver, true); err != nil {
			if s.logLevel >= 1 {
				log.Printf("SetMulticastLoopback %s: %v\n", newm, err)
			}
		}
		newm.conn = conn
		s.mifcs[k] = newm
		go s.udpListener(newm)
		s.watchedLock.RLock()
		for sdn := range s.subscribed {
			newm.sendQuestion([]dns.Question{{sdn, dns.TypePTR, dns.ClassINET}})
		}
		s.watchedLock.RUnlock()
	}
	return highesthwaddr, nil
}
func (s *MDNS) SetOutgoingTTL(ttl uint32) {
	s.update <- updateRequest{ttl: ttl}
}
func (s *MDNS) udpListener(ifc *multicastIfc) {
	if s.logLevel >= 1 {
		log.Printf("MDNS listening on %s with %v", ifc, ifc.addresses)
	}
	b := make([]byte, 2048)
	for ifc.run() && s.run() {
		n, a, err := ifc.conn.ReadFromUDP(b)
		if err != nil {
			if s.logLevel >= 1 {
				log.Printf("error reading from udp: %v", err)
			}
		}
		msg := new(dns.Msg)
		if !msg.Unpack(b[0:n]) {
			if s.logLevel >= 1 {
				log.Printf("couldn't unpack %d byte dns msg from %v", n, a)
			}
		} else {
			s.fromNet <- &msgFromNet{ifc, a, msg}
		}
	}
}
func (s *MDNS) setAlarms() {
	s.stopAlarms()
	alarm := (s.ttl - 1) / 2
	if alarm == 0 {
		alarm = 1
	}
	s.refreshAlarm = time.NewTicker(time.Duration(alarm) * time.Second)
	if alarm > 3 {
		alarm = 3
	}
	s.cleanupAlarm = time.NewTicker(time.Duration(alarm) * time.Second)
}
func (s *MDNS) stopAlarms() {
	if s.refreshAlarm != nil {
		s.refreshAlarm.Stop()
	}
	if s.cleanupAlarm != nil {
		s.cleanupAlarm.Stop()
	}
}
func serviceFQDN(service string) string {
	if strings.HasSuffix(service, ".") {
		return service
	}
	return "_" + service + "._tcp.local."
}
func instanceFQDN(instance, service string) string {
	if strings.HasSuffix(instance, ".") {
		return instance
	}
	return instance + "." + serviceFQDN(service)
}
func instanceUnqualify(instance, service string) string {
	return strings.TrimSuffix(instance, "."+serviceFQDN(service))
}
func hostFQDN(host string) string {
	if strings.HasSuffix(host, ".") {
		return host
	}
	return host + ".local."
}
func hostUnqualify(host string) string {
	host = strings.TrimSuffix(host, ".local.")
	return strings.TrimSuffix(host, ".")
}
func hostport(host string, port uint16) string {
	return fmt.Sprintf("%s:%d", host, port)
}
func serviceFQDNFromInstanceFQDN(instance string) string {
	pieces := strings.SplitAfterN(instance, ".", 2)
	if len(pieces) != 2 {
		return ""
	}
	return pieces[1]
}
func (s *MDNS) answerA(m *msgFromNet, q dns.Question, msg *dns.Msg) {
	if q.Name == hostFQDN(s.hostName) {
		m.mifc.appendHostAddresses(msg, s.hostName, dns.TypeA, s.ttl)
		return
	}
	for _, set := range s.services {
		for _, req := range set {
			if q.Name == hostFQDN(req.host) && req.port > 0 {
				m.mifc.appendHostAddresses(msg, req.host, dns.TypeA, s.ttl)
				return
			}
		}
	}
}
func (s *MDNS) answerAAAA(m *msgFromNet, q dns.Question, msg *dns.Msg) {
	if q.Name == hostFQDN(s.hostName) {
		m.mifc.appendHostAddresses(msg, s.hostName, dns.TypeAAAA, s.ttl)
		return
	}
	for _, set := range s.services {
		for _, req := range set {
			if q.Name == hostFQDN(req.host) && req.port > 0 {
				m.mifc.appendHostAddresses(msg, req.host, dns.TypeAAAA, s.ttl)
				return
			}
		}
	}
}
func (s *MDNS) answerPTR(m *msgFromNet, q dns.Question, msg *dns.Msg) {
	for service, set := range s.services {
		if q.Name == serviceFQDN(service) {
			for _, req := range set {
				m.mifc.appendDiscoveryRecords(msg, service, req.host, req.port, req.txt, s.ttl)
			}
			return
		}
	}
}
func (s *MDNS) answerSRV(m *msgFromNet, q dns.Question, msg *dns.Msg) {
	for service, set := range s.services {
		for _, req := range set {
			if q.Name == instanceFQDN(req.host, service) {
				m.mifc.appendSrvRR(msg, service, req.host, req.port, s.ttl)
				if req.port > 0 {
					m.mifc.appendHostAddresses(msg, req.host, dns.TypeALL, s.ttl)
				}
			}
		}
	}
}
func (s *MDNS) answerTXT(m *msgFromNet, q dns.Question, msg *dns.Msg) {
	for service, set := range s.services {
		for _, req := range set {
			if q.Name == instanceFQDN(req.host, service) {
				m.mifc.appendTxtRR(msg, service, req.host, req.txt, s.ttl)
			}
		}
	}
}
func (s *MDNS) answerQuestionFromNet(m *msgFromNet) {
	msg := newDnsMsg(0, true, true)
	for _, q := range m.msg.Question {
		switch q.Qtype {
		case dns.TypeA:
			s.answerA(m, q, msg)
		case dns.TypeAAAA:
			s.answerAAAA(m, q, msg)
		case dns.TypePTR:
			s.answerPTR(m, q, msg)
		case dns.TypeSRV:
			s.answerSRV(m, q, msg)
		case dns.TypeTXT:
			s.answerTXT(m, q, msg)
		case dns.TypeALL:
			s.answerA(m, q, msg)
			s.answerAAAA(m, q, msg)
			s.answerPTR(m, q, msg)
			s.answerSRV(m, q, msg)
			s.answerTXT(m, q, msg)
		}
	}
	if len(msg.Answer) > 0 {
		m.mifc.sendMessage(msg)
	}
}
func (s *MDNS) refresh() {
	if len(s.services) > 0 {
		for service, set := range s.services {
			for _, req := range set {
				for _, mifc := range s.mifcs {
					mifc.announceService(service, req.host, req.port, req.txt, s.ttl)
				}
			}
		}
	} else if len(s.hostName) > 0 {
		for _, mifc := range s.mifcs {
			mifc.announceHost(s.hostName, s.ttl)
		}
	}
}
func (s *MDNS) mainLoop() {
	for s.run() {
		select {
		case m := <-s.fromNet:
			if m.msg.Response {
				if s.logLevel >= 2 {
					log.Printf("%s: response %v\n", s.hostName, m.msg)
				}
				if s.isDoppelGanger(m.msg.Answer) {
					if s.logLevel >= 1 {
						log.Printf("%s: name collision, %s also claims to be %s\n", s.hostName, m.sender, s.hostFQDN)
					}
					continue
				}
				for _, rr := range m.msg.Answer {
					if m.mifc.cache.Add(rr) {
						s.changedRR(rr)
					}
				}
			} else {
				if s.hostName == "" {
					break
				}
				if s.logLevel >= 2 {
					log.Printf("%s: question %v\n", s.hostName, m.msg)
				}
				s.answerQuestionFromNet(m)
			}
		case req := <-s.announce:
			set := s.services[req.service]
			if set == nil {
				set = make(map[string]announceRequest)
				s.services[req.service] = set
			}
			set[hostport(req.host, req.port)] = req
			if s.logLevel >= 1 {
				log.Printf("adding service %s %s %d\n", req.service, req.host, req.port)
			}
			for _, mifc := range s.mifcs {
				mifc.announceService(req.service, req.host, req.port, req.txt, s.ttl)
			}
		case req := <-s.goodbye:
			set := s.services[req.service]
			if set != nil {
				delete(set, hostport(req.host, req.port))
			}
			if len(set) == 0 {
				delete(s.services, req.service)
			}
			if s.logLevel >= 1 {
				log.Printf("removing service %s %s %d\n", req.service, req.host, req.port)
			}
			for _, mifc := range s.mifcs {
				mifc.announceService(req.service, req.host, req.port, req.txt, 0)
			}
		case req := <-s.lookup:
			for _, mifc := range s.mifcs {
				mifc.cache.Lookup(req.name, req.rrtype, req.rc)
			}
			close(req.rc)
		case req := <-s.update:
			if len(req.host) > 0 {
				s.hostName = req.host
				s.hostFQDN = hostFQDN(s.hostName)
				s.refresh()
			}
			if req.ttl > 0 && req.ttl != s.ttl {
				s.ttl = req.ttl
				s.setAlarms()
			}
			if req.done != nil {
				close(req.done)
			}
		case <-s.refreshAlarm.C:
			s.refresh()
		case <-s.cleanupAlarm.C:
			for _, mifc := range s.mifcs {
				rrs := mifc.cache.CleanExpired()
				for _, rr := range rrs {
					s.changedRR(rr)
				}
			}
		}
	}
}
func (s *MDNS) Stop() {
	s.doneLock.Lock()
	s.done = true
	s.doneLock.Unlock()
	s.update <- updateRequest{}
	s.stopAlarms()
	for _, mifc := range s.mifcs {
		mifc.conn.Close()
	}
}
func (s *MDNS) run() bool {
	s.doneLock.Lock()
	defer s.doneLock.Unlock()
	return !s.done
}
func (s *MDNS) AddService(service, host string, port uint16, txt ...string) error {
	if len(service) == 0 {
		return errors.New("service name cannot be null")
	}
	if len(host) == 0 {
		if s.hostName == "" {
			return errors.New("AddService requires a host name")
		}
		host = s.hostName
	} else {
		host = hostUnqualify(host)
	}
	s.announce <- announceRequest{service, host, port, txt}
	return nil
}
func (s *MDNS) RemoveService(service, host string, port uint16, txt ...string) error {
	if len(service) == 0 {
		return errors.New("service name cannot be null")
	}
	if len(host) == 0 {
		if s.hostName == "" {
			return errors.New("RemoveService requires a host name")
		}
		host = s.hostName
	} else {
		host = hostUnqualify(host)
	}
	s.goodbye <- announceRequest{service, host, port, txt}
	return nil
}
func (s *MDNS) ResolveRR(dn string, rrtype uint16) []dns.RR {
	dn = hostFQDN(dn)
	rrs := make([]dns.RR, 0)
	for i := 0; i < 3; i++ {
		req := lookupRequest{dn, rrtype, make(chan dns.RR, 10)}
		s.lookup <- req
		for rr := <-req.rc; rr != nil; rr = <-req.rc {
			rrs = append(rrs, rr)
		}
		if len(rrs) > 0 || i >= 3 {
			break
		}
		q := make([]dns.Question, 1)
		q[0] = dns.Question{dn, rrtype, dns.ClassINET}
		for _, mifc := range s.mifcs {
			mifc.sendQuestion(q)
		}
		time.Sleep(50 * time.Millisecond)
	}
	return rrs
}
func (s *MDNS) resolveAddressFromCache(dn string, rrmap map[string]net.IP, minttl uint32) uint32 {
	req := lookupRequest{dn, dns.TypeALL, make(chan dns.RR, 10)}
	s.lookup <- req
	for rr := <-req.rc; rr != nil; rr = <-req.rc {
		switch rr := rr.(type) {
		case *dns.RR_A:
			ip := AtoIP(rr)
			rrmap[ip.String()] = ip
		case *dns.RR_AAAA:
			ip := AAAAtoIP(rr)
			rrmap[ip.String()] = ip
		}
	}
	return minttl
}
func (s *MDNS) ResolveAddress(dn string) ([]net.IP, uint32) {
	dn = hostFQDN(dn)
	rrmap := make(map[string]net.IP, 0)
	minttl := uint32(7 * 24 * 60 * 60)
	for i := 0; i < 3; i++ {
		minttl = s.resolveAddressFromCache(dn, rrmap, minttl)
		if len(rrmap) != 0 || i >= 3 {
			break
		}
		q := make([]dns.Question, 2)
		q[0] = dns.Question{dn, dns.TypeA, dns.ClassINET}
		q[1] = dns.Question{dn, dns.TypeAAAA, dns.ClassINET}
		for _, mifc := range s.mifcs {
			mifc.sendQuestion(q)
		}
		time.Sleep(50 * time.Millisecond)
	}
	var ips []net.IP
	for _, ip := range rrmap {
		ips = append(ips, ip)
	}
	return ips, minttl
}
func (s *MDNS) SubscribeToService(service string) {
	serviceDN := serviceFQDN(service)
	q := []dns.Question{{serviceDN, dns.TypePTR, dns.ClassINET}}
	s.watchedLock.Lock()
	s.subscribed[serviceDN] = true
	s.watchedLock.Unlock()
	s.mifcsLock.RLock()
	defer s.mifcsLock.RUnlock()
	for _, mifc := range s.mifcs {
		mifc.sendQuestion(q)
	}
}
func (s *MDNS) UnsubscribeFromService(service string) {
	serviceDN := serviceFQDN(service)
	s.watchedLock.Lock()
	delete(s.subscribed, serviceDN)
	s.watchedLock.Unlock()
}
type ServiceInstance struct {
	Name   string
	SrvRRs []*dns.RR_SRV
	TxtRRs []*dns.RR_TXT
}
func (s *MDNS) ResolveInstance(instance, service string) ServiceInstance {
	si := ServiceInstance{Name: instanceUnqualify(instance, service)}
	dn := instanceFQDN(instance, service)
	for _, rr := range s.ResolveRR(dn, dns.TypeSRV) {
		switch rr := rr.(type) {
		case *dns.RR_SRV:
			si.SrvRRs = append(si.SrvRRs, rr)
		}
	}
	for _, rr := range s.ResolveRR(dn, dns.TypeTXT) {
		switch rr := rr.(type) {
		case *dns.RR_TXT:
			si.TxtRRs = append(si.TxtRRs, rr)
		}
	}
	return si
}