如何找到我的多播 (Bonjour) 主机名?

问题描述 投票:0回答:5

我已使用 Bonjour API 注册了一条记录。现在我想知道我刚刚发布的记录的内容。我通过指定 NULL 主机名创建它,意思是“使用守护进程的默认值”,但我找不到简单的方法来查询它是什么!

使用 avahi,这很简单:我调用

avahi_client_get_host_name()
来获取机器主机名的起始值。

对于 avahi 和 Bonjour,SRV 记录的值可以在注册生命周期内更改 - 如果注册是使用 NULL 主机名完成的,则记录的主机名会在必要时自动更新。我在这里想要的只是一种在执行注册时获取主机名的initial值的方法。

请注意,在我的 Snow Leopard 测试机器上,默认多播主机名与 gethostname(2) 中的机器名称

相同。

我能想到的四种解决方案:

  1. 在我的进程中获取主机名。它可能就在那里的某个地方。我对进程的内存转储进行了
    strings(3)
    搜索,并在我的地址空间中发现了多播主机名的四个实例,但这可能是巧合,因为该名称用于其他用途。即使我想要的字符串在我的进程中的某个地方,我也找不到 API 来正常地检索它。
  2. 从守护进程查询主机名。可能有一些查询我可以通过 mach 端口发送到获取它的守护进程?我又找不到 API 了。相关代码块位于 mDNSResponder 中的 uDNS.c 文件中,并且似乎没有通过 RPC 接口公开。
  3. 我可以查找我注册的服务。不过,这可能会涉及一些网络流量,因此除非有某种保证不会发生,否则我不愿意这样做。
  4. 重新实现uDNS.c中的逻辑。它从以下组合中获取机器的主机名:

    • 动态 DNS 配置
    • 静态配置的多播主机名
    • 反向查找主接口的 IPv4 地址
    • 它特别不使用
      gethostname(2)
      或等效物

    重新实现该逻辑似乎不可行。

目前,我倾向于进行查找以获取初始 SRV 注册的值,但这似乎并不理想。正确的解决办法是什么?

macos networking bonjour zeroconf mdns
5个回答
2
投票

我需要这样做。您想要使用 ConvertDomainNameToCString 宏(包含在 mDNSEmbeddedAPI.h 中),并且需要访问核心 mDNS 结构。

以下是获取已注册的确切 Bonjour/Zeroconf 主机名的方法:

char szHostname[512];
extern mDNS m;

ConvertDomainNameToCString(&m.MulticastHostname, szHostname);

希望这对您有帮助。


0
投票

为了记录,我选择了 (4),获取机器的配置以将守护进程正在使用的主机名组合在一起,而无需查询它。

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;
}

0
投票

在命令行中,可以使用

scutil
命令获取本地 (Bonjour) 主机名:

scutil --get LocalHostName

以编程方式,可以通过

kSCPropNetLocalHostName
框架使用
SystemConfiguration
键获得此功能。


0
投票

我知道这个问题已经得到了回答,但我根据皮尔兹在答案后半部分所说的内容,去了解如何使用 SystemConfiguration 框架来做到这一点。

这就是我所做的工作,我想这可能会节省其他人在谷歌上搜索的时间: 在斯威夫特:

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)")
        }
    }

0
投票

我尝试了这里列出的一些方法,特别是 SCDynamicStoreCreate( CFSTR("ipmenu"),...); SCDynamicStoreCopyValue( ..., CFSTR("设置:/网络/主机名")); CFDictionaryGetValue( ..., kSPCropNetLocalHostName),但发现 C 语言中的 CF API 确实很混乱。(我避免将整个内容拼写为代码片段,以免诱使某人将其粘贴到他们的应用程序中。它不我觉得不可靠。)我不能排除返回的一些 CFPropertyList 实际上是数组而不是 CFDictionary/CFString。最后,经过一番摸索之后,我返回的字符串仍然是错误的,因为它需要在其与 DNSService 在本地发布的内容匹配之前附加“.local”。所以,我觉得这个问题还没有得到彻底解决。

虽然原发帖者特别询问如何在不执行服务请求的情况下执行此操作,但我认为最好的方法实际上是执行服务请求并从 DNSResponder 本身获取字符串 - 您可以使用 DNSServiceBrowse() 浏览您的自定义计算机主要网络接口上的注册表名称。浏览回调应调用 DNSServiceResolve(),然后您将正确的值作为解析回调的第 6 个参数。

剩下的唯一问题是找到要使用的网络接口。虽然我希望条条大路通罗马,但您可以使用 Apple 平台上的 nw_path_monitor 找出主要使用哪个外部网络接口。您需要将接口类型 (nw_path_uses_interface_type) 与 nw_path_enumerate_interfaces 给出的接口类型进行匹配,以选择正确的接口类型。该接口上的 nw_interface_get_index 提供与 DNSServiceBrowse 的第三个参数一起使用的索引。

© www.soinside.com 2019 - 2024. All rights reserved.