昨日の延長上の話。ある IP アドレスが、どのサブネットに属しているかがわかれば、あとはそのサブネットがどの国に割り当てられたものかがわかればいい。
cidr.txt
何はともあれ、IP アドレスの国別割り当て状況がわからないと話にならない。でも、世の中良くしたもんで、データを公開してくれている人がいた。ありがたい。
このページから、cidr.txt をダウンロードした。
CIDR ってのは Classless Inter-Domain Routing の略で、サブネットマスクを使って IP アドレスの割り当てとルーティングをする仕組みのこと。また、例えば 192.168.1.0/24 というサブネットの表記を CIDR表記というようだ。簡単な説明はここ。
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