If I ping an IP address like ping 8.8.8.8 how is there any DNS lookup here? Does ping -n 8.8.8.8 do something different?
It may depend on the implementation. If ping without -n includes dns.google in its output, ping -n probably doesn't, this is the difference. But it's possible that neither output includes the string.
In my Ubuntu 18.04.4 LTS man 8 ping reads:
ping is part of iputils package and the latest versions are available in source form at http://www.skbuff.net/iputils/iputils-current.tar.bz2.
This exact address seems not to work, still http://www.skbuff.net/iputils provides other links, including the one to SourceForge. I have downloaded iputils-s20151218 and read the code.
-n is handled in ping_common.c:
case 'n':
options |= F_NUMERIC;
break;
Then the option (options & F_NUMERIC) is important in ping.c:
pr_addr(__u32 addr)
{
…
if (exiting || (options & F_NUMERIC) ||
!(hp = gethostbyaddr((char *)&addr, 4, AF_INET)))
sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
else {
…
To get to gethostbyaddr, both exiting and options & F_NUMERIC must be false (because a || b does will not evaluate b if a is true). The latter depends on whether -n was or wasn't used.
gethostbyaddr is the "attempt to lookup symbolic names for host addresses". See man 3 gethostbyaddr. This is the same library call getent hosts uses when you provide an IP address (see man 1 getent).
See the difference:
$ getent hosts 8.8.8.8
8.8.8.8 dns.google
$ ping -c 1 dns.google
…
64 bytes from dns.google (8.8.8.8): icmp_seq=1 ttl=120 time=10.6 ms
…
$ ping -n -c 1 dns.google
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=11.3 ms
…
$
It may seem ping without -n just uses the string the user provided; but no:
$ getent hosts poczta.wp.pl
193.17.41.99 poczta.wp.pl
$ getent hosts 193.17.41.99
193.17.41.99 poczta.o2.pl
$ ping -c 1 poczta.wp.pl
…
64 bytes from poczta.o2.pl (193.17.41.99): icmp_seq=1 ttl=60 time=9.71 ms
…
$
Here poczta.wp.pl was resolved to 193.17.41.99 and then 193.17.41.99 was resolved to poczta.o2.pl (note o2 instead of wp). Usage of -n would suppress the latter step.
For some addresses this happens:
$ getent hosts superuser.com
151.101.1.69 superuser.com
151.101.193.69 superuser.com
151.101.65.69 superuser.com
151.101.129.69 superuser.com
$ getent hosts 151.101.1.69 151.101.193.69 151.101.65.69 151.101.129.69
$ # the output was empty
$ ping -c 1 superuser.com
…
64 bytes from 151.101.1.69 (151.101.1.69): icmp_seq=1 ttl=58 time=37.1 ms
…
$ ping -n -c 1 superuser.com
…
64 bytes from 151.101.1.69: icmp_seq=1 ttl=58 time=36.8 ms
…
$
But if I provide a numeric address then there will be no difference:
$ getent hosts 8.8.8.8
8.8.8.8 dns.google
$ ping -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=10.8 ms
…
$ ping -n -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=8.91 ms
…
$
It's because of this fragment from ping.c:
if (inet_aton(target, &whereto.sin_addr) == 1) {
hostname = target;
if (argc == 1)
options |= F_NUMERIC;
} else
inet_aton converts from the IPv4 numbers-and-dots notation into binary form. It returns 1 on success. If the last argument given to ping can be converted, the fragment evaluates options |= F_NUMERIC as if -n was used.
I actually compiled ping from the source in two versions: the original and with if … options |= F_NUMERIC; commented out. The modified version behaves like this:
$ ./ping -c 1 8.8.8.8
…
64 bytes from dns.google (8.8.8.8): icmp_seq=1 ttl=120 time=9.67 ms
…
$ ./ping -n -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=8.69 ms
…
$
Now I can explicitly answer the question:
Does ping -n 8.8.8.8 do something different [than ping 8.8.8.8]?
No. The address in the IPv4 numbers-and-dots notation makes ping (from iputils on Linux) work as if -n was used.
If I ping an IP address like ping 8.8.8.8 how is there any DNS lookup here?
I created a separate network namespace (to make sure as little traffic as possible interferes) with a veth pair, then used wireshark there. (If you want to replicate my results and need help with the procedure, see this answer, example 2).
With the original ping:
ping -n -c 1 8.8.8.8 generates ICMP echo request and receives ICMP echo reply. No DNS involved.
ping -c 1 8.8.8.8 does the same (no surprise here, explained above).
This means there is no DNS lookup. Below are some tests for comparison.
With my modified ping:
ping -n -c 1 8.8.8.8 behaves like the original.
ping -c 1 8.8.8.8 queries the DNS server after ICMP and receives a response. This is to get dns.google.
Again with the original ping:
ping -n -c 1 dns.google queries the DNS server and receives a response before ICMP. This is to translate dns.google to 8.8.8.8 or 8.8.4.4.
ping -c 1 dns.google queries the DNS sever and receives a response before ICMP (to translate to 8.8.8.8 or to 8.8.4.4) and separately after ICMP (to translate back).