IPアドレスがどこの国に割り当てられたものかを調べる

昨日の延長上の話。ある IP アドレスが、どのサブネットに属しているかがわかれば、あとはそのサブネットがどの国に割り当てられたものかがわかればいい。

cidr.txt

何はともあれ、IP アドレスの国別割り当て状況がわからないと話にならない。でも、世の中良くしたもんで、データを公開してくれている人がいた。ありがたい。

 cf. 世界の国別 IPv4 アドレス割り当てリスト

このページから、cidr.txt をダウンロードした。
CIDR ってのは Classless Inter-Domain Routing の略で、サブネットマスクを使って IP アドレスの割り当てとルーティングをする仕組みのこと。また、例えば 192.168.1.0/24 というサブネットの表記を CIDR表記というようだ。簡単な説明はここ。

 cf. CIDRとは – IT用語辞典 e-Words

cidr.txt は国名コードと割り当てられているサブネットの CIDR表記の一覧になっている。冒頭の10行はこうなっている。

AD 85.94.160.0/19
AD 91.187.64.0/19
AD 109.111.96.0/19
AD 185.4.52.0/22
AD 194.158.64.0/19
AE 2.48.0.0/14
AE 5.30.0.0/15
AE 5.32.0.0/17
AE 5.38.0.0/17
AE 5.53.96.0/21

国名コードと CIDR表記のサブネットはタブ文字で区切られている。
ちなみに AD はアンドラ、AE はアラブ首長国連邦だそうだ。

Ruby版

cidr.txt を読み込んでハッシュに保存しておき、入力された IP アドレスが属するサブネットを探して、見つかったらその国名コードを出力する。cidr.txt が実行ディレクトリにおいてあるのを前提としている。

# coding: utf-8

def main
  cidr = load_cidr("cidr.txt")
  addr = ARGV.shift
  cidr.each do |subnet, country|
    if in_subnet?(addr, subnet)
      puts country
      break
    end
  end
end

def load_cidr(file)
  cidr = {}
  File.open(file) do |f|
    f.each do |line|
      co, subnet = line.chomp.split("\t")
      cidr[subnet] = co
    end
  end
  cidr
end

def ip_to_b(addr)
  addr.split(".").map{|x| sprintf("%08d", x.to_i.to_s(2).to_i)}.join("")
end

def in_subnet?(addr, subnet)
  addr_b = ip_to_b(addr)
  subnet_addr, mask = subnet.split("/")
  subnet_addr_b = ip_to_b(subnet_addr)
  mask = mask.to_i
  addr_b.slice(0...mask) == subnet_addr_b.slice(0...mask)
end


main

IP アドレスの属するサブネットの検索が力技だけど、まあいいか。
実行結果。

takatoh@nightschool $ ruby -rresolv -e 'puts Resolv.getaddress("blog.panicblanket.com")'
210.224.185.170
takatoh@nightschool $ ruby addr_to_co.rb 210.224.185.170
JP

というわけで、このブログの IP アドレスは日本のものだってことが分かった。

Python版

同じやり方。

# coding: utf-8

import sys

def main():
    cidr = load_cidr("cidr.txt")
    addr = sys.argv[1]
    for subnet, co in cidr.items():
        if is_in_subnet(addr, subnet):
            print co
            break

def load_cidr(file):
    cidr = {}
    f = open(file)
    for line in f:
        co, subnet = line.rstrip().split("\t")
        cidr[subnet] = co
    return cidr

def ip_to_b(addr):
    return "".join(map(lambda x: "%08d" % int(bin(int(x))[2:]), addr.split(".")))

def is_in_subnet(addr, subnet):
    addr_b = ip_to_b(addr)
    subnet_addr, mask = subnet.split("/")
    subnet_addr_b = ip_to_b(subnet_addr)
    mask = int(mask)
    return addr_b[:mask] == subnet_addr_b[:mask]


main()
takatoh@nightschool $ python addr_to_co.py 210.224.185.170
JP

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください