I have been having issues trying to get JmDNS working on Mac OS X. The symptom is that I can discover any services on the network except the ones on my own computer. It doesn't matter if they are on localhost or on a virtual machine running on my computer - in either case, they just don't come back from the list
call.
I managed to condense what we're doing down to a test which passes on Windows but fails on Mac OS X. Now the problem is that I can't figure out where the problem is.
@Test
public void testAdvertisingOverLoopback() throws Exception
{
// happens on any address but loopback is the strangest
InetAddress address = InetAddress.getLoopbackAddress();
String type = "_test._tcp.local.";
String name = "test-service";
int port = 9999;
Map<String, String> properties = ImmutableMap.of("key", "value");
// simulate the service starting up. issue also occurs in separate VMs
try (JmDNS serviceDns = JmDNS.create(address))
{
serviceDns.registerService(ServiceInfo.create(type, name, port,
0, 0, properties));
try (JmDNS clientDns = JmDNS.create(address))
{
ServiceInfo[] services = clientDns.list(type);
// One of the entries should:
assertThat(services, is(arrayContaining(allOf(
// Contain an address which matches the one we advertised (culling those which might
// be registered by other tests which happen to run at the same time.)
hasProperty("inetAddresses", arrayContaining(sameAddressAs(address))),
// Match the parameters we specified in the call to list.
hasProperty("application", equalTo("test")),
hasProperty("protocol", equalTo("tcp")),
hasProperty("domain", equalTo("local")),
// Match the info we advertised.
hasProperty("port", equalTo(9999)),
hasCustomProperty("key", "value")
))));
}
}
}
private static Matcher<InetAddress> sameAddressAs(final InetAddress address)
{
return new TypeSafeMatcher<InetAddress>()
{
@Override
protected boolean matchesSafely(InetAddress inetAddress)
{
return Arrays.equals(address.getAddress(), inetAddress.getAddress());
}
@Override
public void describeTo(Description description)
{
description.appendText("same address as ");
description.appendValue(address.getHostAddress());
}
};
}
private static Matcher<ServiceInfo> hasCustomProperty(final String key,
final String value)
{
return new TypeSafeMatcher<ServiceInfo>()
{
@Override
protected boolean matchesSafely(ServiceInfo serviceInfo)
{
return value.equals(serviceInfo.getPropertyString(key));
}
@Override
public void describeTo(Description description)
{
description.appendText("has custom mDNS property ");
description.appendValue(key);
description.appendText(" = ");
description.appendValue(value);
}
};
}
In the debugger, I can see that it is not binding the socket to any particular address, just a particular port. But it is then setting it to a specific interface.
What I see in Wireshark is that the packets are coming out from my machine's public IP (the address of en0) even though lo0 is the interface I'm using for the test. I also see both the query and the response packets. The responses are coming back.
But then on the Java side, I see it calling DatagramSocket#receive(DatagramPacket)
and never getting a packet.
(I also spent half the day looking for alternatives for JmDNS, but it looks like the other libraries which are claiming to be replacements for it can't actually do multicast yet, which makes them a bit pointless. :( )
What is going on here?