brutella / dnssd

This library implements Multicast DNS (mDNS) and DNS-Based Service Discovery (DNS-SD) for Zero Configuration Networking in Go.
MIT License
203 stars 32 forks source link

Howto: query meta service _services._dns-sd._udp.local #20

Open chinenual opened 4 years ago

chinenual commented 4 years ago

I'm trying to send out a unicast QU for service discovery at startup of my application. I can't find a way to use the library to do that. LookupType() does not expose a way to set the unicast bit and unconditionally sends out a QM query.

I tried to copy and paste the browse.go functions into my app and adapt them, but got stuck since I can't create a Query struct (all fields are unexported) and I can't find a public constructor function.

Ideas?

(if you agree that this is a reasonable use case and adding a call to setQuestionUnicast() in lookupType is the fix, I could create a pull request. Perhaps create a new func LookupTypeUnicast() that passes in a boolean to tell lookupType() to set the bit?)

chinenual commented 4 years ago

To recap: I'm trying to fix behavior in the current library that causes a LookupType() to not see some services that have already registered themselves with mDNS prior to my application startup. I've observed another application that appears to solve this by sending out a _services._dns-sd._udp query at startup - and gets answers for everything on the network.

.... actually I think my proposed solution may not be sufficient (see below for how it behaves):

I am using wireshark to try to understand how a general bonjour browser works (Discovery on macos). I see it send out a QU query for _services._dns-sd._udp.local and gets Answers for everything on the network.

I've made a local copy of the dnssd module with the proposed fix:


func LookupTypeUnicast(ctx context.Context, service string, add AddServiceFunc, rmv RmvServiceFunc) (err error) {
    conn, err := newMDNSConn()
    if err != nil {
        return err
    }
    defer conn.close()

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

func lookupType(ctx context.Context, service string, conn MDNSConn, add AddServiceFunc, rmv RmvServiceFunc, unicast bool) (err error) {
...
    if unicast {
        setQuestionUnicast(&m.Question[0])
    }

and call it like this from my application:

                serviceType := "_services._dns-sd._udp.local."
        logger.Infof("ZEROCONF: serviceDiscovery %s\n", serviceType)
        if err = dnssd.LookupTypeUnicast(ctx, serviceType, addFn, rmvFn); err != nil {
            if strings.Contains(err.Error(), "context deadline exceeded") {
                logger.Debugf("ZEROCONF: ListenFor %s %v\n", serviceType, err)
            } else {
                logger.Errorf("ZEROCONF: ListenFor %s %v\n", serviceType, err)
            }
            return
        }

I can see the query via wireshark and it looks identical to the one I see from Discovery -- however mine has no answers. Is there something else I need to do ?

chinenual commented 4 years ago

I have created a pull request that implements unicast queries. It solves my "missing answer at app startup" bug. (despite my previous comment suggesting that it was not sufficient, I've been testing since and it does appear to solve the problem).

https://github.com/brutella/dnssd/pull/21

brutella commented 4 years ago

Thanks for your effort. I've made some significant changes to the library in the refactoring branch. Your issue might already be fixed in that branch.

chinenual commented 3 years ago

I peeked at the refactoring branch and don't see anything similar.

FWIW: I've had to change my forked library to be based on the released v1.1.1 tag rather than master. When I run with master, my application chews up 100% CPU continuously while browsing. I've not been able to profile the library to determine why. v1.1.1 does not have this problem.

After testing this for several days, I am no longer sure that this Unicast browse really solves my problem. Even with the unicast query, I am seeing frequent cases where a service started before my application is not seen by the query until that service is restarted.

brutella commented 3 years ago

I've experienced that unicast responses are never received #15. That's why the library do not use them – see probe.go

I was not able to figure out why this happens. Any ideas?

chinenual commented 3 years ago

I just tried commenting out the SetQuestionUnicast() in my fork -- unfortunately it doesn't seem to improve things.

I spent a considerable time staring at Wireshark logs trying to understand what makes my app using dnssd different than, for example, the Discovery bonjour browser application in the mac store. Other than the unicast difference I could not spot a difference. I really don't know how to diagnose this any further.

What's weird is that for my application, I browse for two different service types. One always works. The other rarely works. Both work 100% reliably in the Discovery app.

_osc._udp.local. reliably responds immediately (the app is touchosc running on two different ios devices). The other is a JUCE-based VST (_synergia._tcp.local.) running on the localhost which rarely responds unless I restart it after I've started my browse. I've reviewed the library that app is using (https://github.com/Anthony-Nicholls/jucey_bonjour); it seems to be directly using Apple's SDK so I have no reason to suspect it's doing something wrong...

I'm open to any and all ideas of how to debug/diagnose this!