読者です 読者をやめる 読者になる 読者になる

IPアドレスから地域を特定する2 - GeoLite Legacy Country CSV

前回、GeoLite2 と GeoIP2 Java API 等のライブラリを使って IP アドレスから国と都市を特定しましたが、今回は GeoLite Legacy の Country CSV ファイル (IPv4用) を使って国を特定する処理を実装してみます。

なお、前回は IPv6 でも処理できましたが、今回は IPv4 のみを処理対象としています。

今回のソースは http://github.com/fits/try_samples/tree/master/blog/20141008/

はじめに

GeoLite Legacy の Country CSV ファイル GeoIPCountryWhois.csv のフォーマットは下記のようになっています。

Country CSV フォーマット
<開始IP>,<終了IP>,<開始IPの数値>,<終了IPの数値>,<国名コード>,<国名>

内容は下記の通りです。

GeoIPCountryWhois.csv
"1.0.0.0","1.0.0.255","16777216","16777471","AU","Australia"
"1.0.1.0","1.0.3.255","16777472","16778239","CN","China"
"1.0.4.0","1.0.7.255","16778240","16779263","AU","Australia"
"1.0.8.0","1.0.15.255","16779264","16781311","CN","China"
"1.0.16.0","1.0.31.255","16781312","16785407","JP","Japan"
・・・

3・4 列目の数値は IP アドレスを 32bit の正の整数値で表現したものです。

なお、GeoIPCountryWhois.csv ファイルは GeoLite Legacy Downloadable Databases の GeoLite Country の CSV/zip からダウンロードできます。

国の判定

GeoIPCountryWhois.csv ファイルを使った国の判定は下記のように処理できます。

  • (1) 指定の IPv4 アドレスを数値化
  • (2) GeoIPCountryWhois.csv ファイルの "開始IPの数値" と "終了IPの数値" との間に (1) が含まれている行の国名を返す

注意点として、IPv4 を数値化した値は 32bit の正の整数ですが、Java に unsigned int のような型はありませんので、long 型などで扱う事になります。

また、Inet4AddresshashCode() メソッドで (1) の値を取得できるのですが、unsigned な値ではありませんので下記のような方法で変換します。 (例えば、IP アドレス 150.70.96.0 を数値化した 2521194496 は、Java の int 型では -1773772800 となります)

  • (a) Java 8 から追加された Integer.toUnsignedLong(int) メソッドを使用
  • (b) 0xffffffff と AND 演算する (<Inet4Address の hashCode 値> & 0xffffffff
get_country.groovy
if (args.length < 2) {
    println '<geolite country csv file> <ip address>'
    return
}

// (a)
def toNumForIP = { Integer.toUnsignedLong(it.hashCode()) }
// (b) 以下でも可
// def toNumForIP = { it.hashCode() & 0xffffffff }

def ip = toNumForIP( InetAddress.getByName(args[1]) )

new File(args[0]).eachLine() {
    def r = it.replaceAll('"', '').split(',')

    def from = r[2] as long
    def to = r[3] as long

    if (from <= ip && ip <= to) {
        println r.last()
        System.exit(0)
    }
}

println 'Unknown'

実行結果は下記の通りです。

実行結果1
> groovy get_country.groovy GeoIPCountryWhois.csv 1.21.127.254

Japan
実行結果2
> groovy get_country.groovy GeoIPCountryWhois.csv 223.255.254.1

Singapore
実行結果3
> groovy get_country.groovy GeoIPCountryWhois.csv 192.168.1.1

Unknown