DNS queries are cached permanently, how to reliably clear DNS cache on Android 7+? Is there a reliable, reproducible way to clear DNS cache on Android Nougat 7.0?

I know there is at least one other question like this but that one was from 8 years ago and I hope I can ask again and see what people are doing these days.

To explain a bit more my setup:

I am testing a simple web page loading in Chrome browser with Wireshark packet capture to see the DNS requests. I am also running dnsmasq on my LAN and have mapped my webpages name: mymovies to the correct IP address of the machine on my LAN that the web server is on. So http://mymovies should work and it does load.

The DNS lookups initially start with the DHCP server appending the DNS suffix (.home) so in the packet trace I see a DNS request for mymovies.home which fails because I don't have an IP address mapping for that in dnsmasq.conf. So a second DNS lookup for just mymovies appears and is successful.

Mostly I find that subsequent DNS lookups are always just for mymovies and never again for mymovies.home no matter what I try to clear the DNS cache:



I am testing over WiFi with a Moto G5 running Android 7.0 and it is rooted so I can run commands from within adb shell to the OS.

I have tried lots of things like:


Closing down Chrome browser and starting it up again.
chrome://net-internals/#dns to clear host cache and flushing the sockets too
Settings > Apps > Chrome > Storage > Clear Cache
ndc resolver clearnetdns wlan0


The only thing that seemed to work for me was turning the phone off and on again. After restart I opened http://mymovies in Chrome on the phone. The page loaded and in the Wireshark trace I see the first DNS request for mymovies.home which fails as usual followed by a DNS request for mymovies that succeeds. I then closed down that tab and opened a new one and loaded the page again.. I did this twice. Each time now I see a dns request for mymovies only.. it doesn't even try mymovies.home.

I restarted dnsmasq server and the first DNS lookup after that was for mymovies.home followed by a lookup for mymovies. But since then the DNS lookups are now only ever for mymovies. This is despite restarting the dnsmasq server a few times and trying the four so-called DNS cache clearing steps listed above.

But to confuse the matters, I tried the trick of switching into and back out of Airplane Mode. Now when I load my little test web page a few times it doesn't seem to cache at all anymore? Loading the webpage always initially gives a DNS look up for mymovies.home which fails then a lookup for mymovies that succeeds. Even after restarting the phone the DNS lookups have now stopped caching completely and it firstly tries mymovies.home every time!

I think this caching behavior is normal and expected but I'd just like a way to clear that cache so it goes back to trying mymovies.home without me having to restart the phone.

I'm not sure if the IPv6 has anything to do with things? In my router I have specified the IP address of the Raspberry Pi on my LAN that dnsmasq is running on so the router directs DNS requests to it.

I don't want the page to stop caching OR to always cache. I just want to be able to clear the DNS cache in a predictable manner so I can make the DNS request for mymovies.home appear once followed by the expected DNS caching.
HOW DNS QUERY AND CACHING WORKS?


  Mostly I find that subsequent DNS lookups are always just for mymovies and never again for mymovies.home




NOTE: Just to make sure, check that .home search domain is set:

~$ dumpsys netd | grep 'search domains:'
        search domains: home




This behavior is due to Negative DNS Caching. The question is not explicitly about Android but more about the understanding of how DNS works. In short:


When you type mymovies in a web browser (assuming that the browser doesn't cache DNS), Android's DNS resolver checks domain &lt;--> IP mapping in local cache (netd) and /etc/hosts. Then it makes a query (for A record) to the IP address of DNS server it received from DHCP (or you set manually), which is dnsmasq in your case. As standard is to append suffix first, so mymovies.home is queried first. See details in How does Android OS do DNS name resolution?
dnsmasq checks static mapping in hosts and dnsmasq.conf file. If no result found, it forwards query to upstream DNS server, whatever configured in dnsmasq.conf or /etc/resolv.conf (or whatever mechanism the system uses) - say 1.1.1.1.
After receiving a successful or failed response, dnsmasq answers the client (Android phone) along with Time To Live (TTL) value.
Neither dnsmasq nor 1.1.1.1 is able to resolve mymovies.home because .home is not a known Top Level Domain Name (TLD) to any authoritative server. So a NXDOMAIN response ("which means that the queried domain name does not exist in the DNS") is returned by root server (or some intermediate recursive caching server). Response includes an SOA record with high TTL (and SOA.MINIMUM field) value - say 5 hours.
dnsmasq caches the NXDOMAIN response with 5 hours expiry and forwards the same to Android.
Android's resolver caches the same for 5 hours, but that time may get doubled in some situations; see some explanation here. Also How long does negative DNS caching typically last?.
Android's resolver makes another query for (A RR of) mymovies, which succeeds at dnsmasq and is returned with a smaller TTL which must be 0 in your case (see option --local-ttl in dnsmasq man).


So Android's resolver will make next query for mymovies.home after 5 hours, and for mymovies after 0 seconds. If you want that mymovies.home should be tried again sooner, define a static mapping, which can possibly be --address=/mymovies.home/ to get NXDOMAIN from dnsmasq with no TTL. Or to have some fun, setup NXDOMAIN Redirection locally.



DNS TROUBLESHOOTING:

I'll suggest you to simplify your setup. You don't necessarily need Wireshark, just use --log-queries option to see how dnsmasq responds to queries. Also once get rid of cache COMPLETELY on Android and DNS server. But note that you may still end up getting a cached response from some recursive non-iterative server, like if your ISP does DNS interception.


  I have tried lots of things like:
  
  
  chrome://net-internals/#dns to clear host cache and flushing the sockets too
  


Clearing browser's DNS cache doesn't clear OS's system-wide cache (maintained by netd), though both need to be cleared. Better try testing with some native tool (like dig or nslookup) which doesn't rely on system's resolver and doesn't do caching itself. On Termux, for instance:

~$ dig mymovies @&lt;dnsmasq_IP&gt;
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR
;; ANSWER SECTION:
mymovies.com.       0   IN  A   192.168.1.124


~$ dig mymovies.home @&lt;dnsmasq_IP&gt;
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NXDOMAIN
;; AUTHORITY SECTION:
.           10678   IN  SOA a.root-servers.net. nstld.verisign-grs.com. 2020010901 1800 900 604800 86400


Note NOERROR response and the TTL (0) of positive answer, while NXDOMAIN response, TTL (10678) and SOA.MINIMUM (86400) of negative answer.


  Even after restarting the phone the DNS lookups have now stopped caching completely and it firstly tries mymovies.home every time!


However on the very next query (dig mymovies.home @&lt;dnsmasq_IP&gt;), there would be no AUTHORITY SECTION. It's because SOA RR (the source of negative TTL) is not cached (as per RR format): "SOA records are always distributed with a zero TTL to prohibit caching". But dnsmasq is doing negative caching (see option --no-negcache), so this time it's responding with NXDOMAIN from its own cache, but with no TTL.

That's why if you clear cache on Android, it then keeps on querying mymovies as well as mymovies.home because both are not being cached on Android due to zero TTL, unless you restart dnsmasq or 10678 seconds pass. To verify, send SIGUSR1 to dnsmasq and see in log the expiry time against mymovies.home cache entry. However some other DNS servers may behave differently.



CLEARING DNS CACHE:


  
  ndc resolver clearnetdns wlan0
  


This command clears DNS server, not DNS cache. See How to configure DNS properly?.


  Is there a reliable, reproducible way to clear DNS cache on Android Nougat 7.0?


Yes, but not a straightforward command-line method. From the perspective of negative caching, we don't have anything like unbound-control flush_negative on Android. ndc resolver flushnet &lt;network_ID&gt; was removed in Android 7, though flushDnsCache still exists. Neither we can call _resolv_flush_cache_for_net from command-line. Services network_management and netd_listener also don't provide any such method.


However setResolverConfiguration (part of netd service) states: "Flushes the cache as needed (i.e. when the servers ... change)". This can also be called using ndc:

~$ ndc resolver clearnetdns &lt;network_ID&gt;
~$ ndc resolver setnetdns &lt;network_ID&gt; home &lt;dmsmasq_IP&gt;


* network_ID can be obtained using dumpsys netd | grep Default

So the cache should be cleared with DNS server reset. Additionally sending broadcast android.intent.action.CLEAR_DNS_CACHE (flushVmDnsCache) might also be required to clear DVM's cache (or kill the apps). A simple explanation can be found here.

But I'm not sure if this method works on all Android releases.
Turning Wi-Fi OFF and ON (or Airplane Mode) must work as network_ID is destroyed and a new one is created.
Or restart the networking / caching daemon (netd). This also restarts dependent services (including zygote), so causes a soft reboot and kills all running apps:

~# setprop ctl.restart netd

There's also a hack to bypass / disable DNS caching entirely using environment variable ANDROID_DNS_MODE=local. But it also disables search domains (.home suffix in your case).