Occasionally I’ll come across a tool, library or service that solve a problem. Each ‘spotlight’ is a quick post highlighting one of these and how it can be used.

Various website profess to tell you your IP address and location. Often they also tell you ‘your location is being exposed!!!’ and try to sell you VPN software to hide your location.

Or sometimes you’ll get an email saying ‘you logged in from a new IP address at location XXX, YYY’.

How do they match up an IP addresses to a location?

The easy way

If you want to know the general part of the world, it’s pretty trivial - IANA publish a list of top-level allocations at IANA IPv4 Address Space Registry, so you can look up the first octet of an IP address and find out which RIR (Regional Internet registry) manages it.

In my case my IP address starts with 81., so I can check 081/8 and find RIPE NCC, which is the Réseaux IP Européens , so I’m probably somewhere in ‘Europe, the Middle East, [or] parts of Central Asia’.

Source: Wikimedia commons (link)

Getting granular

What if you want to know in more detail? Say to the level of a country, or a city?

Various companies offer databases of IP ranges and location estimates, and where they’ve been assigned to, using a combination of public information and spot-testing.

Two such companies offering free versions of their databases are:

Both offer files in MMDB formats (we’ll come on to this in a sec), both require signing up to access the database.

If you really don’t want to sign up, this repo on GitHub by P3TERX downloads and re-uploads the database on a daily basis. I’m not endorsing or suggesting you use this - just noting it exists!

Once you’ve got a file in MMDB format, the mmdbctl command by IPinfo can be used to look up an IP address.

For example, you could look up one of the IP addresses for the website of the German Federal Government, by first using dig to look up the IP address. We can see that the IP address is indeed registered in Germany:

dig +short www.bundesregierung.de
#     185.173.230.38
#     185.173.231.38
mmdbctl read 185.173.230.38 ./GeoLite2-Country.mmdb

Output:

{
  "continent": {
    "code": "EU",
    "geoname_id": 6255148,
    "names": {
      "de": "Europa",
      "en": "Europe",
      "es": "Europa",
      "fr": "Europe",
      "ja": "ヨーロッパ",
      "pt-BR": "Europa",
      "ru": "Европа",
      "zh-CN": "欧洲"
    }
  },
  "country": {
    "geoname_id": 2921044,
    "is_in_european_union": true,
    "iso_code": "DE",
    "names": {
      "de": "Deutschland",
      "en": "Germany",
      "es": "Alemania",
      "fr": "Allemagne",
      "ja": "ドイツ連邦共和国",
      "pt-BR": "Alemanha",
      "ru": "ФРГ",
      "zh-CN": "德国"
    }
  },
  "registered_country": {
    "geoname_id": 2921044,
    "is_in_european_union": true,
    "iso_code": "DE",
    "names": {
      "de": "Deutschland",
      "en": "Germany",
      "es": "Alemania",
      "fr": "Allemagne",
      "ja": "ドイツ連邦共和国",
      "pt-BR": "Alemanha",
      "ru": "ФРГ",
      "zh-CN": "德国"
    }
  }
}

For programmatic access, there’s a Rust library (and probably libraries for other languages), which I’ll demonstrate in a future article.