PHP

PEAR::Net_Whois で IP アドレスから国名を調べる関数

アクセス元のホストの国籍を Apache のログからアクセス解析して、って頼まれたけど、どうしたらいいのかわからなくてスクリプトを書いてみた。
いろいろ調べたら、PHP の PEAR::Net_Whois を使えば、Whois 情報を取得できることがわかった。
サーバーに PEAR::Net_SocketPEAR::Net_Whois をインストールする。

$ pear install Net_Socket
$ pear install Net_Whois

でも、$whois->query() を行うときに、検索先の Whois サーバを指定しないといけない。
InterNIC のサイトを見る限り、IP アドレスの第 1 オクテット目ごとに、地域インターネット レジストラ (RIR) が決まっているらしい。(IP アドレスの割り当て状況)
それを参考に IP アドレスから、PEAR::Net_Whois を行う関数を作った。以下の通り。

function getWhoisFromIPAddr($ipaddr){
//Include PEAR::Net_Whois
require_once("Net/Whois.php");
$whois = new Net_Whois;
//Split IPaddr into 4 octets.
preg_match("/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})(.*)/",$ipaddr,$oct);
//Choose WHOIS server by 1st octet.
if($oct[1]<=40){ $server="whois.arin.net"; }
elseif($oct[1]==41){ $server="whois.afrinic.net"; }
elseif($oct[1]<=57){ $server="whois.arin.net"; }
elseif($oct[1]<=61){ $server="whois.apnic.net"; }
elseif($oct[1]==62){ $server="whois.ripe.net"; }
elseif($oct[1]<=61){ $server="whois.apnic.net"; }
elseif($oct[1]<=76){ $server="whois.arin.net"; }
elseif($oct[1]<=95){ $server="whois.ripe.net"; }
elseif($oct[1]<=113){ $server="whois.arin.net"; }
elseif($oct[1]<=126){ $server="whois.apnic.net"; }
elseif($oct[1]<=132){ $server="whois.arin.net"; }
//  elseif($oct[1]==133){ $server="whois.apnic.net"; }
elseif($oct[1]<=140){ $server="whois.arin.net"; }
elseif($oct[1]==141){ $server="whois.ripe.net"; }
elseif($oct[1]<=144){ $server="whois.arin.net"; }
elseif($oct[1]==145){ $server="whois.ripe.net"; }
elseif($oct[1]<=149){ $server="whois.arin.net"; }
elseif($oct[1]==150){ $server="whois.apnic.net"; }
elseif($oct[1]==151){ $server="whois.ripe.net"; }
elseif($oct[1]==152){ $server="whois.arin.net"; }
elseif($oct[1]==153){ $server="whois.apnic.net"; }
elseif($oct[1]==154){ $server="whois.afrinic.net"; }
elseif($oct[1]<=162){ $server="whois.arin.net"; }
elseif($oct[1]==163){ $server="whois.apnic.net"; }
elseif($oct[1]<=170){ $server="whois.arin.net"; }
elseif($oct[1]<=171){ $server="whois.apnic.net"; }
elseif($oct[1]<=185){ $server="whois.arin.net"; }
elseif($oct[1]<=187){ $server="whois.lacnic.net"; }
elseif($oct[1]<=188){ $server="whois.ripe.net"; }
elseif($oct[1]<=191){ $server="whois.lacnic.net"; }
elseif($oct[1]==192){ $server="whois.arin.net"; }
elseif($oct[1]<=195){ $server="whois.ripe.net"; }
elseif($oct[1]==196){ $server="whois.afrinic.net"; }
elseif($oct[1]<=199){ $server="whois.arin.net"; }
elseif($oct[1]<=201){ $server="whois.lacnic.net"; }
elseif($oct[1]<=203){ $server="whois.apnic.net"; }
elseif($oct[1]<=209){ $server="whois.arin.net"; }
elseif($oct[1]<=211){ $server="whois.apnic.net"; }
elseif($oct[1]<=213){ $server="whois.ripe.net"; }
elseif($oct[1]<=216){ $server="whois.arin.net"; }
elseif($oct[1]<=217){ $server="whois.ripe.net"; }
elseif($oct[1]<=222){ $server="whois.apnic.net"; }
elseif($oct[1]<=255){ $server="whois.arin.net"; }
else;
//Exec query to chose WHOIS server.
if($server){ return($reply=$whois->query($ipaddr,$server)); }
else{ return(0); }
}

133 で始まる IP アドレスといえば専ら、日本政府の機関、日本の国立大学法人 (うちの大学もそう)、日本の IT 企業のアドレスだから、APNIC なはずだけど、エラーがでるので、ARIN に問い合わせしている。
本来なら、エラーメッセージをもとに 2 次検索すべきなんだろうけど面倒なので省略する。
Whois 情報が返ってくるので、それを引数にしてに以下の関数を実行する。

function getCountryByWhois($result){
preg_match("/country:( )*([a-zA-Z]{1,3})/i",$result,$country);
if($country[2]){ return($country[2]); }
else{ return(0); }
}

2 つの関数は、以下のようにして使える。

echo("Who is 133.62.198.174 ?<br />\n");
$result=getWhoisFromIPAddr("133.62.198.174");
echo("Country: ".getCountryByWhois($result));

実行すると、「Country: JP」とカントリーコードが出力される。
蛇足だけど、IP アドレスから会社名や法人名を調べるには、JPNIC のサーバ (whois.nic.ad.jp) に対して、$whois->query() を行えばよい。海外の企業も各国の下位レジストラの Whois サーバに置き換えれば検索できる。

erf, erfc (誤差関数, 相補誤差関数) を求める PHP の関数を作った

erf, erfc (誤差関数, 相補誤差関数) を求める PHP の関数
PHP には erf(), erfc() がなかったので、Ruby に Math.erf(), Math.erfc() として実装されている C のソース を PHP に移植しました。

関数には困らないはずの PHP に統計処理に必要な関数がなかったので、 Ruby 本体のソースコード (C 言語) を探してきて PHP で書き直した。
数値計算を伴うプログラムでは言語を選ぶときに、数学関数の豊富さも考慮したほうがよさそう。Ruby 勉強しようかな。