Reputation: 9685
I've registered a record using the Bonjour API. Now I want to know the contents of the record I just published. I created it by specifying a NULL hostname, meaning, "use the daemon's default", but I can't find a simple way to query what that is!
With avahi, it's easy: I call avahi_client_get_host_name()
to get the starting value of the machine's hostname.
For both avahi and Bonjour, the value of the SRV record can change during the lifetime of the registration - if the registration was done with a NULL hostname, the record's hostname is updated automatically when necessary. All I want here is a way to get the initial value of the hostname, at the time when I perform the registration.
Note that on my Snow Leopard test machine, the default multicast hostname is not the same as the machine's name from gethostname(2)
.
Four solutions I can think of:
strings(3)
search on a memory dump of my process, and found four instances of the multicast hostname in my address space, but that could be coincidence given the name is used for other things. Even if the string I'm after is in my process somewhere, I can't find an API to retrieve it sanely.Re-implement the logic in uDNS.c. It grabs the machine's hostname from a combination of:
gethostname(2)
or equivalentRe-implementing that logic seems infeasible.
At the moment, I'm tending towards doing a lookup to grab the value of the initial SRV registration, but it doesn't seem ideal. What's the correct solution?
Upvotes: 4
Views: 2107
Reputation: 21
I tried some of the approaches listed here, in particular SCDynamicStoreCreate( CFSTR("ipmenu"),...); SCDynamicStoreCopyValue( ..., CFSTR("Setup:/Network/HostNames")); CFDictionaryGetValue( ..., kSCPropNetLocalHostName), but found it a real mess with CF APIs in C. (I'm avoiding spelling out the whole thing as a code snippet so as not to tempt someone just paste it into their app. It doesn't feel reliable to me.) I couldn't rule out that some of the CFPropertyLists returned were actually arrays instead of CFDictionary/CFString. Finally, after some hunting around in the dark, the string I got back was still wrong because it needed ".local" to be appended to it before it matched what the DNSService was advertising locally. So, I feel this is not robustly solved.
While the original poster specifically asks how to do this without doing a service request, I feel the best method is in fact to do a service request and get the string from DNSResponder itself -- You can use DNSServiceBrowse() to browse for your custom registry name on the machine's predominant network interface. The Browse callback should call DNSServiceResolve(), and then you have your correct value as the 6th argument to the resolve callback.
The only remaining issue is finding which network interface to use. While I expect all roads lead to Rome on this and maybe just passing 0 is enough, you can figure out which external network interface is in predominant use using nw_path_monitor on Apple platforms. You will need to match the interface type (nw_path_uses_interface_type) against the types of interfaces given by nw_path_enumerate_interfaces, to pick the right one. nw_interface_get_index on that interface provides the index to use with DNSServiceBrowse's third parameter, noting of course that it is possible that the machine is connected to multiple networks through multiple network adaptors.
Upvotes: 0
Reputation: 91
I know this is already answered, but I went looking to see how to do it with the SystemConfiguration framework based on what Pierz said in the second half of his answer.
This is what I got working, figured it might save someone else googling this some time: In Swift:
import SystemConfiguration
let store = SCDynamicStoreCreate(nil, "ipmenu" as CFString, nil, nil )
if let hostNames = SCDynamicStoreCopyValue(store, "Setup:/Network/HostNames" as CFString){
if let hostName:String = (hostNames[kSCPropNetLocalHostName]) as? String {
print("Host name:\(hostName)")
}
}
Upvotes: 0
Reputation: 8118
From the command line one can obtain the local (Bonjour) hostname using the scutil
command:
scutil --get LocalHostName
Programmatically this is obtainable using the kSCPropNetLocalHostName
key via the SystemConfiguration
Framework.
Upvotes: 0
Reputation: 9685
For the record, I went with (4), grabbing the machine's configuration to pull together the hostname the daemon is using without having to query it.
static char* getBonjourDefaultHost()
{
char* rv = 0;
#ifdef __APPLE__
CFStringRef name = SCDynamicStoreCopyLocalHostName(NULL);
if (name) {
int len = CFStringGetLength(name);
rv = new char[len*4+1];
CFStringGetCString(name, rv, len*4+1, kCFStringEncodingUTF8);
CFRelease(name);
}
// This fallback is completely incorrect, but why should we care...
// Mac does something crazy like <sysctl hw.model>-<MAC address>.
if (!rv)
rv = GetHostname(); // using gethostname(2)
#elif defined(WIN32)
CHAR tmp[256+1];
ULONG namelength = sizeof(tmp);
DynamicFn<BOOL (WINAPI*)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD)>
GetComputerNameExA_("Kernel32", "GetComputerNameExA");
if (!GetComputerNameExA_.isValid() ||
!(*GetComputerNameExA_)(ComputerNamePhysicalDnsHostname, tmp, &namelength))
tmp[0] = 0;
// Roughly correct; there's some obscure string cleaning mDNSResponder does
// to tidy up funny international strings.
rv = tmp[0] ? strdup(tmp) : strdup("My Computer");
#elif defined(__sun)
// This is exactly correct! What a relief.
rv = GetHostName();
#else
#error Must add platform mDNS daemon scheme
#endif
return rv;
}
Upvotes: 0
Reputation: 46
I needed to do exactly this. You want to use the ConvertDomainNameToCString macro (included in mDNSEmbeddedAPI.h), and you need access to the core mDNS structure.
Here's how you get the exact Bonjour/Zeroconf hostname that was registered:
char szHostname[512];
extern mDNS m;
ConvertDomainNameToCString(&m.MulticastHostname, szHostname);
I hope this helps you.
Upvotes: 2