Current section
Files
Jump to
Current section
Files
lib/ip2country.ex
defmodule IP2Country do
@moduledoc """
IP2Country is a simple geolocating module (IP -> Country Code) with built-in database.
Database is provided by https://db-ip.com, and it is compiled into the module. Compilation time is about
20 seconds on 2-core machine.
To update the database, download it from https://db-ip.com/db/download/country, unzip and replace the file
located in `lib/db`.
"""
@db "db/dbip-country.csv"
@external_resource @db
import IP2Country.Converters
IO.puts "Compiling DBIP database, please be patient"
dbip_list = File.stream!(Path.join([__DIR__, @db]), [], :line)
|> Stream.filter(&String.contains?(&1, ".")) # check if it is a line with IPv4
|> Stream.map(&decode_line/1)
|> Stream.map(fn [_ip1, ip2, country] -> {ip2integer(ip2), String.to_atom(country)} end)
|> Enum.sort()
@dbip_list dbip_list
@doc """
Retrieves country code based on the IP address.
Expects string with a proper IP as an argument. Currently works only with IPv4.
Returns atom with the Country Code, uppercased, or `:ZZ` for private addresses.
## Examples
iex> IP2Country.whereis("51.254.116.38")
:FR
iex> IP2Country.whereis("176.9.20.99")
:DE
iex> IP2Country.whereis("83.144.118.166")
:PL
iex> IP2Country.whereis("0.0.0.0")
:ZZ
iex> IP2Country.whereis("255.255.255.255")
:ZZ
iex> IP2Country.whereis("2.17.212.254")
:PL
iex> IP2Country.whereis("2.17.212.255")
:PL
iex> IP2Country.whereis("2.17.213.0")
:SE
iex> IP2Country.whereis("2.17.213.1")
:SE
"""
@spec whereis(String.t) :: atom
def whereis(ip) when is_binary(ip) do
ipint = ip2integer(ip)
{_, country} = Enum.find(@dbip_list, fn {ip2, _} -> ipint <= ip2 end)
country
end
end