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

はざまブログ

cybozu.com の中の人の個人ブログ

IPアドレスから国を引いてみる

IPアドレスから国や地域を調べる Web API は色々ありますね。
手元にIPアドレスから国を引くデータベースが欲しくなったので作ってみたのですが、
こんなのでいいのかどうか自信がないので公開してみます。

☆元データ

hostip.info で公開しているデータがあるよと某氏に教えてもらったのでまず試してみました。あるにはあるんですが、データベースに重複するエントリーがあったり(同じサブネットで国や地域が違うとか)、致命的だったのはサイボウズ本社のIPが別の国になっていたりとかで諦めました。

で、国レベルでいいなら RIR (地域インターネットレジストリ) から公開されている delegation 情報で十分じゃないかなと考えて、そちらをダウンロードしてみました。例えば APNIC ならここにあります。

☆データベース設計

IPアドレスはサブネット単位で管理されているので、IPアドレス全部を逆引きできるようにする必要はありません。B-tree なデータベースであれば対象となるIPアドレスより小さい最近接アドレスの国情報を調べればOK。SQLでいうとこんな感じ。

CREATE TABLE IF NOT EXISTS ip2country (
  ip INT UNSIGNED PRIMARY KEY,
  country CHAR(2) NOT NULL
);

ip カラムにはMySQL なら INET_ATON 関数で数値に変換したアドレスを入れます。1レコードにつきこれなら 4 + 2 = 6 バイトですね。

☆コンパクション

実際のサブネット割り当てを眺めると、US や JP には相当な範囲で連続して割り当てられています。上記のデータベース設計では連続する同じ国のレコードは一番小さなサブネットひとつあれば済みます。

というわけで、delegation 情報から抜き出したサブネットのアドレスをソートして、同じ国コードが連続するときは一番小さなサブネットしか出力しないことにすると、データベースのサイズを小さくできます。SQLのファイルサイズで 1MB 強にできました。

☆完成

というわけで、できたのでひとつ調べてみます。前述の hostip.info のダウンロードサイトのひとつ、ドイツのサイトを試してみました。

$ nslookup hostip.consolut.com
Name:   hostip.consolut.com
Address: 188.40.49.10

mysql> select INET_NTOA(ip), country FROM ip2country WHERE ip < INET_ATON("188.40.49.10") ORDER BY ip DESC LIMIT 1;
+---------------+---------+
| INET_NTOA(ip) | country |
+---------------+---------+
| 188.40.0.0    | DE      |
+---------------+---------+
1 row in set (0.00 sec)

うまく引けたようです。

☆ソース

delegation 情報を処理するスクリプト貼っとこうかと思ったんですが、はてなブログにファイルを貼る方法がわかりませんので、省略。まあごく簡単なものなのでお察しくださいということで。

作るのに1~2時間程度だったんですが、簡単すぎたのでもっとまともなデータソースを使わないと精度でないんじゃないかと不安です。突っ込みあれば @ymmt2005 までお願いします。