/dev/posts/

Using dig as a LLMNR or mDNS CLI lookup utility

Published:

Updated:

I was looking for a LLMNR commandline lookup utility. Actually, dig can do the job quite fine.

Warning: deprecated (not working anymore)

Update 2023-09-22: The commands using dig (which were a convenient hack) do not work anymore with recent versions of dig (since v9.18).

TL:DR

LLMNR lookup:

dig +noedns -p 5355 @224.0.0.252 wpad
resolvectl query -p llmnr wpad

mDNS lookup:

dig -p 5353 @224.0.0.251 example.local
resolvectl query -p mdns example.local
avahi-resolve-host-name example.local

Choosing the network interface:

LLMNR usage

LLMNR (Link-Local Multicast Name Resolution), RFC4795, is a Microsoft-centric DNS-based protocol for resolving names using multicast on the local network. It is expected to be used by default for single label names only:

By default, an LLMNR sender SHOULD send LLMNR queries only for single-label names. Stub resolvers supporting both DNS and LLMNR SHOULD avoid sending DNS queries for single-label names, in order to reduce unnecessary DNS queries.

In the Windows world, it is used alongside with other protocols[3] such as NBNS/NBT-NS (NetBios/TCP Name Service, RFC1001, RFC1002), WINS and DNS. Apparently, LLMNR is tried before NBNS but it isnot really documented as far as I know.

LLMNR is quite similar in spirit to mDNS (multicast DNS, RFC6762). mDNS is originated from Apple Bonjour where it is used with DNS-SD (DNS Service Discovery, RFC6783) and has been available on Linux systemd for a long time through Avahi. It is normally used for domain names in the local domain (eg. foo.local, foo.bar.local, etc.).

On Linux systems, in addition to Avahi, system-resolved supports both LLMNR and mDNS/DNS-SD.

LLMNR lookup with dig

I was looking for a CLI tool for resolving names with LLMNR but could not find any. In fact, dig can be the job well. LLMNR is really DNS with a few changes:

LLMNR header:

1  1  1  1  1  1
0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  | C|TC| T| Z| Z| Z| Z|   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

DNS header:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

This means we can actually do LLMNR requests with dig. Dor example, we can do a DNS WPAD 😨 request:

dig +noedns -p 5355 @224.0.0.252 wpad

EDNS0 is disabled because the Microsoft LLMNR profile does not support EDNS0:

The Link Local Multicast Name Resolution (LLMNR) Profile [...] does not support Extension Mechanisms for DNS (EDNS0) [RFC2671].

mDNS lookup with dig

Similarly for mDNS, requests are sent to 224.0.0.251 or ff02::fb on UDP port 5353. Full mDNS queries are expected to send queries from this same IP/port tuple (and receive the answer on this same tuple):

A compliant Multicast DNS querier, which implements the rules specified in this document, MUST send its Multicast DNS queries from UDP source port 5353 (the well-known port assigned to mDNS), and MUST listen for Multicast DNS replies sent to UDP destination port 5353 at the mDNS link-local multicast address (224.0.0.251 and/or its IPv6 equivalent FF02::FB).

This cannot be done with dig: it cannot listen for answers sent to a multicast address.

However, the mDNS protocol allows for a simpler client implementation, One-Shot Multicast DNS Queries:

The most basic kind of Multicast DNS client may simply send standard DNS queries blindly to 224.0.0.251:5353, without necessarily even being aware of what a multicast address is. [...] these queries MUST NOT be sent using UDP source port 5353, since using UDP source port 5353 signals the presence of a fully compliant Multicast DNS querier, as described below.

This means we can use dig as well:

dig -p 5353 @224.0.0.251 example.local

Alternatives

Using resolvectl

Alternatively if systemd-resolved is running, resolvectl can be used:

resolvectl query -p llmnr example
resolvectl query -p mdns example.local

Using avahi

mDNS request can be done through the Avahi damone using:

avahi-resolve-host-name example.local

  1. These versions of dig use connect()-ed[1] UDP sockets. When connect() is used on a datagram socket, the socket drops any ingress packet which does not come from the chosen (multicast in our case) IP address. With mDNS and LLMNR, the response comes from the IP address of the responder which is not the multicast address (224.0.0.252 or 224.0.0.251) and will therefore be ignored by the connect()-ed UDP socket. ↩︎

  2. If you wonder how this works, I believe this is because the routing algorithm takes the source address (if any) into account in this case. You can see that by comparing, ip route get 224.0.0.252 (goes through the interface if your default route) and ip route get 224.0.0.252 from 127.0.0.1 (goes through lo). ↩︎

  3. Apparently there is as well PNRP (Peer Name Resolution Protocol) and SNID (Server Network Information Discovery). ↩︎