スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[PHP] xxx.xxx.xxx.xxx/yy の形のIP帯域指定にマッチするかをチェックする

携帯電話でのアクセスなどで、接続元のIPがアクセス許可されるべきものかどうかを判別する際、その許可されるべきIPが "192.168.1.0/24" のような形で示されることがよくあります。
携帯のキャリアが発表している形はたいていこうかと思います。

iモードセンタのIPアドレス帯域
http://www.nttdocomo.co.jp/service/imode/make/content/ip/
例:210.153.84.0/24

EZサーバのIPアドレス帯域
http://www.au.kddi.com/ezfactory/tec/spec/ezsava_ip.html
例:210.230.128.224/28

Yahoo!ケータイにて利用するIPアドレス帯域
http://creation.mb.softbank.jp/web/web_ip.html
例:123.108.236.0/24

少し調べると、正規表現を使うとか、PEAR の Net_IPv4 を使うとか方法はいろいろありそうなのですが、少々ややこしいのでベタな形で関数を作成しました。

ロジックとしては、
・帯域IPをマスク部分でANDをかけたもの
・接続元IPをマスク部分でANDをかけたもの
が等しいかという単純なビット演算です。

たとえば、192.168.1.0/24 と 192.168.1.123 の場合、
11000000 10101000 00000001 00000000 (192.168.1.0)
11000000 10101000 00000001 11111011 (192.168.1.123)
となり、

11000000 10101000 00000001 00000000 (192.168.1.0)
AND演算
11111111 11111111 11111111 00000000 (/24 : 24個の1を頭から)

11000000 10101000 00000001 00000000 (192.168.1.0)

11000000 10101000 00000001 11111011 (192.168.1.123)
AND演算
11111111 11111111 11111111 00000000

11000000 10101000 00000001 00000000 (192.168.1.0)

で、結果が同じか、ということで判断します。

テストは少ししかしていませんので完璧かどうかはいまの時点で不明ですが、理屈的にはわかりやすいものかと思います。






<?php
//===============================================================
// IPを指定の形式の文字列と比較してマッチングするかを確認
// マッチングすれば TRUE
//===============================================================

function CheckIpFromString( $str, $ip ){

//初期化
$mask = "";
$ip_str = "";

//----------------------
//要素に分解
//----------------------
//マスクとIP部と分離

$ary_temp = explode( "/", $str );
if( is_array( $ary_temp ) ){
$mask = isset( $ary_temp[1] ) ? $ary_temp[1] : "";
$ip_str = $ary_temp[0];
}
//形式がおかしい場合(/で分割できない)は、エラーとする
if( strlen( $mask ) <= 0 || strlen( $ip_str ) <= 0 ){
return( false );
}
// 判別文字列
$ary_target = explode( ".", $ip_str );
// アクセス元IP
$ary_access = explode( ".", $ip );

//-------------------------------------------------
//分割に失敗した場合はエラー(NG扱い)として戻す
//-------------------------------------------------
if( !is_array( $ary_target ) || !is_array( $ary_access ) ){

return( false );
}
if( count( $ary_target ) < 4 || count( $ary_access ) < 4 ){
return( false );
}

//----------------------------------
// ビット演算での判定のため2進数に変換する
//----------------------------------

$bin_target = "";
$bin_access = "";
// 数値分だけ頭から1をセット
$bin_mask = str_pad( str_repeat( "1", $mask ), 32, "0", STR_PAD_RIGHT );
// コロンを省いてそれぞれ2進数にしたものを連結
for( $i = 0; $i < 4; $i++ ){
$bin_target .= str_pad( decbin( $ary_target[$i] ), 8, "0", STR_PAD_LEFT );
$bin_access .= str_pad( decbin( $ary_access[$i] ), 8, "0", STR_PAD_LEFT );
}

//-------------------------------
//判定して結果を返す
//-------------------------------
// どちらもマスクでAND演算を行い(1の部分だけが比較対象)、比較を行う

if( bindec( $bin_target & $bin_mask ) == bindec( $bin_access & $bin_mask ) ){
return( true );
}else{
return( false );
}
}

//端末のIPを取得
$ip = $_SERVER['REMOTE_ADDR'];
$result .= "接続元(仮) : <br>\n";
$result .= $ip."<br>\n";

// IP帯域を配列でセット
$ip_str = array(
"210.230.128.224/28",
"121.111.227.160/27",
"61.117.1.0/28",
"219.108.158.0/27",
"219.125.146.0/28",
"192.168.1.0/24"
);

$result .= "指定帯域(複数指定) : <br>\n";
for( $i = 0; $i < count( $ip_str ); $i++ ){
$result .= $ip_str[$i]."<br>\n";
}

//ヒットしたかのフラグ
$flg = 0;

//どれかにヒットするか
for( $i = 0; $i < count( $ip_str ); $i++ ){
//ヒットしたらフラグを立てて抜ける
if( CheckIpFromString( $ip_str[$i], $ip ) ){
$flg = 1;
break;
}
}

if( $flg > 0 ){
//該当する場合
$result .= "OK!";
}else{
//該当せず
$result .= "NG!";
}

//テンプレートをインクルード
include( "void.tmpl" );

?>



追記:20090703
なんか2進になった数値を比較したらキテレツな結果になったので、比較の際に10進数に変換する処理( bindec() )を追加。
関連記事
スポンサーサイト

comment

管理者にだけメッセージを送る

検索フォーム
リンク
最新記事
最新コメント
カテゴリ
RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。