2024-01-19
md
Obtaining the Public IP Address of a Local Network Connected to the Internet

Strictly speaking, there is no need to know the IP address that my Internet service provider (ISP) assigns to the Internet facing interface of the router it installed in my house and rents to me. I use a dynamic DNS service to provide seamless access to some resources on our local area network from outside the house using a fixed host name. The address record of that host name has to be updated whenever the ISP decides to change the assigned IP address, but that's all done automatically without revealing the public IP address of the local network. The DDNS service provider picks up the address from a simple HTTP request sent to it at regular intervals.

However I do like to know what is happening to my system and I also like having backups. Consequently, scripts send out notifications of changes in the IP address of the WAN port of the router. To do that, it is necessary to get that public IP address. This post is about techniques to obtain the public IP address of the local area network. It must be said that the post is about 32-bit IP version 4 addresses. The 128-bit address size of IP version 6 is much too big for my little head.

Table of Content

  1. Getting the Public IP Address of a Local Network
  2. Obtaining the Router WAN IP From Web Sites
  3. Problems with Getting the WAN IP From DNS Servers
  4. Obtaining the Router WAN IP From DNS Servers
  5. Another Approach?
  6. References

Getting the Public IP Address of a Local Network toc

All routers necessarily know the IP address of their WAN interface. WAN is short Wide Area Network, which in the case of households means the Internet. So the WAN interface is the outward facing connection to the Internet via an ISP (Internet Service Provider). Probably all routers show that WAN address in their Web interface as illustrated below.

WAN IP address in Router Web Admin Page
LEDE 17.01.4 || OpenWrt 19.07.7 || SG482220DC on the Home Hub 3000 (a rebadged SageMCOM Fast5566)

One would think that the best way to get the public facing IP address of a router is to query said router. In my, admittedly limited, experience that is very hard to do. I have been unable to find an API that would respond to queries from other devices on the LAN (Local Aarea Network) for any of the above routers. I could not even find a way to download their HTML status page in order to scrap the desired IP address. Presumably, a script could be run on the LEDE/OpenWrt routers to display the WAN IP address in a small Web server accessible from the LAN (example). Such a solution is not applicable for most ISP provided routers such as the Home Hub 3000 installed in our home.

The only generally viable way of accessing the public facing IP address of a router is to obtain it from someone outside the local network. Searching the Web, I have found that two types of services do respond to such queries. The next section is about the first one: specialized HTTP servers of the "what is my IP" kind. Then I'll discuss a problem encountered with that approach despite my best efforts and go on to using DNS queries to get the WAN IP address.

Obtaining the Router WAN IP From Web Sites toc

Search the Web with the "what is my ip" criterion and an abundance of links will be returned. A very well-known search engine said that it had approximately 3,920,000,000 results (in only 0.21 secondes), the third of which was a direct answer from the engine that displayed my very own public IP. My favourite search engine displayed my public IP address at the very top along with a close enough geolocation before starting on its own long list of results. There is no shortage of Web sites that will display the IP address of the ...

For our purpose, we want sites that will return the IP address in the simplest form possible. Optimally the sites should return the IP address only in text/plain MIME type. Testing on Jan. 18, 2024, I found seven such sites. Another two sites add a trailing newline character to the IP address while an additional three sites provide the wanted information but with extra text. Nine of the twelve sites can be reached with an HTTP or an HTTPS request. Two supports HTTP only while ipgrab.io uses the HTTPS protocol only, but it does redirect HTTP requests to HTTPS.

SiteProtocol and LinkResult
ifconfig.me/iphttphttpsIP address only
v4.ident.mehttphttpsIP address only
api.ipify.orghttphttpsIP address only
ipinfo.io/iphttphttpsIP address only
ipecho.net/plainhttphttpsIP address only
myip.dnsomatic.comhttphttpsIP address only
ipv4.whatismyip.akamai.comhttpIP address only
icanhazip.comhttphttps Adds ending newline
ipgrab.iohttps Adds ending newline
ip4only.me/api/httphttpsVerbose text
freedns.afraid.org/dynamic/check.phphttphttpsVerbose text over multiple lines
checkip.dyndns.orghttpVerbose HTML ending with carriage return

Here is a terminal session, in which curl is used to get the response of a couple of the above sites.

michel@hp:~$ curl "https://ipgrab.io" 274.174.74.70 michel@hp:~$ curl "http://ifconfig.me/ip" 274.174.74.70michel@hp:~$

Of course 274.174.74.70 is not my IP address, it's not even a valid IP address.. One could also use wget to the same end as long as the correct command line parameters are used.

michel@hp:~$ wget -q -O- "https://ipgrab.io" 274.174.74.70 michel@hp:~$ wget -q -O- "http://ifconfig.me/ip" 274.174.74.70michel@hp:~$

Note how ipgrab.io appends a line feed to the address while ifconfig.me does not. This is not significant if a bash script is used to obtain the IP address.

#!/usr/bin/bash # List of "What is my IP?" type of Web sites to test # MyIpSites=("ifconfig.me/ip v4.ident.me api.ipify.org ipinfo.io/ip ipecho.net/plain myip.dnsomatic.com icanhazip.com ipgrab.io ip4only.me/api/ freedns.afraid.org/dynamic/check.php checkip.dyndns.org") # Select curl or wget to perform the query # cmd="curl --connect-timeout 1 --max-redir 0 --retry 2 -s" #cmd="wget --timeout=1 --max-redirect=0 --tries=3 -q -O-" #----- no need to modify below ---------------------------- chosen=${cmd%% *} # extract the first word of $cmd echo "Testing \"what is my IP?\" type Web sites with $chosen." echo "Be patient, some sites may not be on line or accept a protocol." for site in $MyIpSites; do echo -e "\n$site:" url="http://$site" res=`$cmd "$url"` echo " http:=>$res<=" url="https://$site" res=`$cmd "$url"` echo " https:=>$res<=" done

The response from each of these site is stored in a string variable named res which is then displayed to the console between "=>" and "<=" which makes it easy to spot completely missing responses and responses with extra characters. It is notable that the extra line feed character is not stored in res.

michel@hp:~$ ./getwanip.sh Testing "what is my IP?" type Web sites with curl. Be patient, some sites may not be on line or accept a protocol. ifconfig.me/ip: http:=>274.174.74.70<= https:=>274.174.74.70<= v4.ident.me: http:=>274.174.74.70<= https:=>274.174.74.70<= api.ipify.org: http:=>274.174.74.70<= https:=>274.174.74.70<= ipinfo.io/ip: http:=>274.174.74.70<= https:=>274.174.74.70<= ipecho.net/plain: http:=>274.174.74.70<= https:=>274.174.74.70<= myip.dnsomatic.com: http:=>274.174.74.70<= https:=>274.174.74.70<= ipv4.whatismyip.akamai.com/: http:=>274.174.74.70<= https:=><= icanhazip.com: http:=>274.174.74.70<= https:=>274.174.74.70<= ipgrab.io: http:=><= https:=>274.174.74.70<= ip4only.me/api/: http:=>IPv4,274.174.74.70,v1.1,,,See http://ip6.me/docs/ for api documentation<= https:=>IPv4,274.174.74.70,v1.1,,,See http://ip6.me/docs/ for api documentation<= freedns.afraid.org/dynamic/check.php: http:=>Detected IP : 274.174.74.70 HTTP_CLIENT_IP : HTTP_X_FORWARDED_FOR : REMOTE_ADDR : 274.174.74.70<= https:=>Detected IP : 274.174.74.70 HTTP_CLIENT_IP : HTTP_X_FORWARDED_FOR : REMOTE_ADDR : 274.174.74.70<= checkip.dyndns.org: <= http:=>Current IP CheckCurrent IP Adress: 274.174.74.70 https:=><=

A trailing carriage return is appended ot the IP addresss by checkip.dyndns.org which explains why the closing "<=" is displayed over the two spaces at the start of the line.

With a Python script, the results are slightly different. First, here is the Python 3 script

#!/usr/bin/python3 # coding: utf-8 # List of "What is my IP?" type of Web sites to test # MyIpSites = ['ifconfig.me/ip', 'v4.ident.me', 'api.ipify.org', 'ipinfo.io/ip', 'ipecho.net/plain', 'myip.dnsomatic.com', 'ipv4.whatismyip.akamai.com', 'icanhazip.com', 'ipgrab.io', 'ip4only.me/api/', 'freedns.afraid.org/dynamic/check.php', 'checkip.dyndns.org'] #----- no need to modify below ---------------------------- from requests import get headers = {'Pragma': 'no-cache', 'Cache-Control' : 'no-cache' } def getIP(proto, site): #print('{}://{}'.format(proto, site)) try: hf = get('{}://{}'.format(proto, site), headers, timeout=1, allow_redirects=False) print(' {} =>{}<='.format(proto, hf.text)) except: print(' {} =>Error<='.format(proto)) print("Testing \"what is my IP?\" type Web sites with request.get") print("Be patient, some sites may not be on line or accept a protocol.") for site in MyIpSites: print('\n{}:'.format(site)) getIP('http', site) getIP('https',site)

And here is the output from that script which is different from the one obtained with the bash script.

michel@hp:~$ ./getwanip.py Testing "what is my IP?" type Web sites with request.get Be patient, some sites may not be on line or accept a protocol. ifconfig.me/ip: http =>274.174.74.70<= https =>274.174.74.70<= v4.ident.me: http =>274.174.74.70<= https =>274.174.74.70<= api.ipify.org: http =>274.174.74.70<= https =>274.174.74.70<= ipinfo.io/ip: http =>274.174.74.70<= https =>274.174.74.70<= ipecho.net/plain: http =>274.174.74.70<= https =>274.174.74.70<= myip.dnsomatic.com: http =>274.174.74.70<= https =>274.174.74.70<= ipv4.whatismyip.akamai.com: http =>274.174.74.70<= https =>Error<= icanhazip.com: http =>274.174.74.70 <= https =>274.174.74.70 <= ipgrab.io: http =><= https =>274.174.74.70 <= ip4only.me/api/: http =>IPv4,274.174.74.70,v1.1,,,See http://ip6.me/docs/ for api documentation <= https =>IPv4,274.174.74.70,v1.1,,,See http://ip6.me/docs/ for api documentation <= ipv4-a.jsonip.com: http =>Error<= https =>Error<= freedns.afraid.org/dynamic/check.php: http =>Error<= https =>Error<= checkip.dyndns.org: http =>Current IP CheckCurrent IP Address: 274.174.74.70 <= https =>Error<=

Python interpreted the appended carriage return by checkip.dyndns.org as a line feed and, more importantly, Python appended the extra line feeds onto the IP address.

Luckily the output from all these sites is simple enough that the IP address can be extracted with a regular expression. I will not go into details here, but you can find the source code for the modified versions of these scripts among my gists: getwanipre.sh and getwanipre.py. I will point out that the regular expression used in both of these scripts is not foolproof. It will gleefully return addresses such as 999.888.777.666 which is obviously not a legal IP address. I also believe it would not return an address such as 42.0129.34.8 that represents a valid 32-bit value even if it contains an extraneous 0. Since there is no reason why one of the above sites should return an invalid or a badly formatted IP address, I will not worry about that.

Problems with Getting the WAN IP From DNS Servers toc

I am not proposing that we should all put on our tin foil hats, but one could question the motive of "What is my IP?" sites. Without any evidence to that effect, I believe that most of these are run by commercial enterprises with revenues from other services which provide the IP address as a service for their clients. The web hosting company for this site does that (it is not included it in the above list because it outputs the IP address in a somewhat complex HTML page). That is an understable situation. Except for dnsomatic.com, akamai.com, afraid.org and dyndns.org, what about the other eight sites in the above list that do not have obvious links to businesses and do not advertise? There is no doubt that the request to obtain the WAN IP address yields a fair amount of information. Some are quite open about that, see InfoByIP.com for example. The gleaned information may have some value and could perhaps be sold by the "what is my ip" web site. After all, someone is aggregating this data and publishing annual reports on the most used operating system and Web browser and so on. Just to alarm readers, here is a 2013 report by the Office of the Privacy Commissioner of Canada What an IP Address Can Reveal About You that shows that IP address can be leveraged to gain even more information.

Let's put this into perspective. There's nothing special about "What is my IP?" sites. Any accessed remote site (including this one by the way!) gets the same information and the owner of that site could lookup the additional information if desired. By the same token, anytime we give our telephone number to someone, that person can turn around and find much more information about us. That is a cost of using the Internet. Most do not seem to mind handing out their email address, telephone number and even more valuable information in some cases just because they are asked. One of the sites in my list appears to be run by an AFOL (I learned something today!) with eclectic interests. Perhaps that person runs the site as a hobby, much like I do here. Even if he or she were to sell the information gathered from the occasional request for my public IP address, I would not begrudge the fraction of a penny earned in that fashion from the information I provide to many, many other sites freely.

The major problem with getting a LAN's public IP address from a "What is my IP?" type of site is that its reliability is not assured by any means. While working on this post, I had to remove the site ipv4-a.jsonip.com which was responding just a few days ago, but as of (2024-01-17) it is not online. Some sites are very clear about their ephemeral nature, even if it is in fine print.

This service may be unavailable at times and may be discontinued at any time without notice. (source)

My mitigation strategy was to query the sites one after the other until a valid response was obtained. I added a couple of enhancements. The load was shared among the sites by shuffling them before starting the sequence of queries. In addition, a notification was sent whenever a site failed to respond. If a site systematically failed to respond then it would be best to remove it from the list or, better yet, replace it with another site. This is exactly what happened, one of the sites became less reliable a few weeks ago, and the mounting number of notifications prompted me to search for more sites and then go on to take another look at other ways to obtain my public IP address.

Obtaining the Router WAN IP From DNS Servers toc

So far, I have found four name servers that can be used to obtain one's own public IP address. They work along the same principle. A request to resolve a specific host name, let's call it whoami_hostname, is forwarded to the name server also called resolver. The latter interprets the specific host name as a request for the source IP address of the request and sends that back. Since the request comes from our local network, the source IP address sent to any outside machine is our router's WAN IP address. That's the magic of NAT (Network Address Translation) which is not going to be discussed here.

Providerwhoami_host nameResolver's own host nameResolver ip addressPublic
Cisco Umbrella*myip.opendns.comresolver1.opendns.com208.67.222.222yes
Cloudflarewhoami.Cloudflare 1.1.1.1yes
Googleo-o.myaddr.l.google.comns1.google.com216.239.32.10no
Akamaiwhoami.akamai.netns1-1.akamaitech.net193.108.88.1no

(*): Created as OpenDNS but renamed OpenDNS Umbrella and then Cisco Umbrella in 2016 since acquired by Cisco in 2015 (source).

Please read Public recursive name server, it the difference between "public" as opposed to "private", "local" or ISP name servers. To determine if the above name servers are public or not, I checked their IP address against the name server list at public-dns.info. Even though the last two name servers are not public, they do reply to my DNS requests. Make of that what you will because I certainly do not know the legal consequences, if any, that can result from using non-public name servers for the express purpose of obtaining the WAN IP address of a router. Being a coward with a vanishingly small legal defence budget, I recommend using only public name servers.

Three utilites, all part of BIND 9 from ICS, that can perform the needed DNS query: dig, nslookup, and host. Here is how to use dig (known as Domain Information Groper until 2017) to query the four name servers. It nevertheless remains easy to identify the name server used.

michel@hp:~$ dig @resolver1.opendns.com myip.opendns.com -4 +short 274.174.74.70 michel@hp:~$ dig @1.1.1.1 whoami.Cloudflare ch txt -4 +short "274.174.74.70" michel@hp:~$ dig @ns1.google.com o-o.myaddr.l.google.com TXT -4 +short "274.174.74.70" michel@hp:~$ dig @ns1-1.akamaitech.net whoami.akamai.net -4 +short 274.174.74.70

If the +short switch is removed from the command, then all the DNS information generated by dig will be displayed. That may help explain why the type of the DNS records returned by Google and Cloudflare require different command line options. There is some irony in using DNS to obtain the IP address of the DNS servers. There has to be an end to that recursion. Indeed, a DNS server host name can be replaced with the corresponding IP address give in the table.

Unfortunately, nslookup does not appear to have a means of providing a terse output.

michel@hp:~$ nslookup myip.opendns.com resolver1.opendns.com Server: resolver1.opendns.com Address: 208.67.222.222#53 Non-authoritative answer: Name: myip.opendns.com Address: 274.174.74.70 michel@hp:~$ nslookup -class=ch -type=txt whoami.Cloudflare 1.1.1.1 Server: 1.1.1.1 Address: 1.1.1.1#53 Non-authoritative answer: whoami.Cloudflare text = "274.174.74.70" Authoritative answers can be found from: michel@hp:~$ nslookup -type=txt o-o.myaddr.l.google.com ns1.google.com Server: ns1.google.com Address: 216.239.32.10#53 o-o.myaddr.l.google.com text = "274.174.74.70" michel@hp:~$ nslookup whoami.akamai.net ns1-1.akamaitech.net Server: ns1-1.akamaitech.net Address: 193.108.88.1#53 Name: whoami.akamai.net Address: 274.174.74.70

The bind9-dnsutils package is not installed by default in the version of Armbian installed on my home automation server. However host found in the bind9-host package is present. So I am particularly interested in using that utility to avoid adding another package to that tiny server with limited storage. In the following examples, I am using the IP address of the name servers instead of their host names.

michel@hp:~$ host -4 myip.opendns.com 208.67.222.222 Using domain server: Name: 208.67.222.222 Address: 208.67.222.222#53 Aliases: myip.opendns.com has address 274.174.74.70 michel@hp:~$ host -c ch -t txt -4 whoami.Cloudflare 1.1.1.1 Using domain server: Name: 1.1.1.1 Address: 1.1.1.1#53 Aliases: whoami.Cloudflare descriptive text "274.174.74.70" michel@hp:~$ host -t txt o-o.myaddr.l.google.com 216.239.32.10 Using domain server: Name: 216.239.32.10 Address: 216.239.32.10#53 Aliases: o-o.myaddr.l.google.com descriptive text "274.174.74.70" michel@hp:~$ host -4 whoami.akamai.net 193.108.88.1 Using domain server: Name: 193.108.88.1 Address: 193.108.88.1#53 Aliases: whoami.akamai.net has address 274.174.74.70

Most of these providers have multiple name servers. OpenDNS was not too imaginative when it comes to setting the host name of its four names servers: resolverX.opendns.com where X=1,2,3,4. Google did much the same: nsX.google.com, X=1,2,3,4. Akamai was more eclectic: ns3-193.akamaitech.net, ns4-193.akamaitech.net, zc.akamaitech.net, zd.akamaitech.net, ze.akamaitech.net, zg.akamaitech.net, zh.akamaitech.net. So substitution of these servers or their specific IP address is certainly possible. I have not managed to find alternate Cloudflare name servers. This question was only worth one whois search per provider to me.

The uptime of Cloudflare or Cisco Umbrella had a very impressive 99.99% uptime in the last 30 days in North America according to DNSPerf. That looks like it will be much better than the "what is my IP?" web sites I have been using so far. However the remaining 0.01% averages to more than a third of a second (360 milliseconds to be precise) of unavailability per hour which is not insignificant. In other words, I will plan on querying the two public name servers in round robin fashion to check my public IP address.

To summarize, here is a short bash script that will get the public IP from the four name servers using the host command.

#!/usr/bin/bash # Obtain WAN IP address with DNS Query # List of command line parameters for the host command # NameServers=("-4 myip.opendns.com resolver1.opendns.com" "-c ch -t txt -4 whoami.Cloudflare 1.1.1.1" "-t txt o-o.myaddr.l.google.com ns1.google.com" "-4 whoami.akamai.net ns1-1.akamaitech.net") LastNameServer=3 #----- no need to modify below ---------------------------- for ((i=0;i<=LastNameServer;i++)); do echo "" echo "host ${NameServers[$i]}:" host ${NameServers[$i]} | tail -1 | grep -oP '\d+(\.\d+){3}' done

The script is available as a gist dnswanip.sh. And here is the output.

michel@hp:~$ ./dnswanip.sh host -4 myip.opendns.com resolver1.opendns.com: 274.174.74.70 host -c ch -t txt -4 whoami.Cloudflare 1.1.1.1: 274.174.74.70 host -t txt o-o.myaddr.l.google.com ns1.google.com: 274.174.74.70 host -4 whoami.akamai.net ns1-1.akamaitech.net: 274.174.74.70

Sorry, but there is no equivalent Python script. In order to simplify the mess of bash and Python scripts presently installed on three machines to handle the dynamic DNS on two local networks, it may be necessary or desirable enough to use Python exclusively. In that case, I will have to learn how to make DNS queries in Python (using dnspython perhaps) and I'll then be in a position to add a script here.

Another Approach? toc

While researching this topic, I ran into some information about getting the WAN IP using the Universal Plug and Play (UPnP)protocol which is often supported by home routers. Here are the references.

I have not tested this, nor do I have plans to test the approach. UPnP has been disabled for years on my router for reasons explained in the Problems and will continue to remain disabled.

References toc

Most of the information presented above can be found on the Web.

Of course, now that I have finished this post, I can find dozens of such resources.