JS|IP範囲の計算方法を理解するため手順をプログラムして追いかける

勘違いしながらドはまりしながら作り上げていくジジイの『JavaScript奮闘記』です。今回はムカつくスパムボットを懲らしめるために必要になるIP範囲の計算ツールを作ります。

ポク太郎です。

よく分からんネットワークの決まり。

IP範囲の計算を理解するため、JavaScriptで作成し、その計算方法を確認・理解します。

キーワードは“サブネットマスクネットワークアドレスホストアドレスブロードキャストアドレスの計算”て感じ。

出来上がったツールはこちら。


IP範囲を求める計算方法

詳細はJavaScriptのコードを追いかけるとして、最初にザザッと大まかに書くと、

IP範囲の求め方・計算方法
1 CIDR表記IP範囲を取得 目的のIPを入力してSearchボタン押すと下にIP範囲やDNSネームが表示されます。Network欄の22.22.22.0/22な感じの表記がIP範囲。/の前がIP、後ろがネットマスク。
2 ネットワークアドレス計算 IPとサブネットマスクを2進数でANDしたもの。
3 ホストアドレス計算 サブネットマスクの後半の2進数での0部分がホストアドレス。
4 ブロードキャストアドレス計算 ネットワークアドレスとホストアドレスの反転を2進数でORしたものがブロードキャストアドレス。
5 IP範囲を求める ネットワークアドレス~ブロードキャストアドレスが求めるIP範囲。

2進数でゴチャゴチャしないといけないので計算方法がよく分からなく。

なので、JavaScriptで計算プログラムを作成→その計算を追いかけ、こちらでそれが正しいことを確認するやり方で計算方法を理解します。

これがIP範囲を計算するJavaScript

22.22.22.0/22な感じのを貼り付けて下の「計算する」を押すと、計算過程と結果が表示&クリップボードにコピーされます。

JavaScript追いかけてIP範囲の計算方法を確認

こちらが上記のソース。

JavaScriptの2進数変換関数を用いるとなおさら訳が分からなく→ここでは01で文字列を作り、IF文にて1文字づつ比較することでANDOR論理を演算するようにしました。

プログラムが読める方は1ステップづつ追いかけると、計算方法が分かりやすいと思います。

見慣れないformat(dt,letter,num)nthf(dt,spr,n)replaceall(dt,a,b)等の関数は自作関数。次項で説明しています。

IP範囲の計算のソースコード

//テキストエリアの内容取得
			var disp = document.getElementById('iptxt').value;
//	document.getElementById('debug').innerHTML = ip;

			//無駄なタブ文字や空白削除
			var tab=String.fromCharCode(9);
			disp = replaceall(replaceall(replaceall(disp,' ',''),' ',''),tab,'');

			var ipdis = nthf(disp,'/',1);
			var ipsub = nthf(disp,'/',2);

			var tmp = '';
	for(var i=0; i<8*4; i++) {
		if (i>ipsub-1){
			tmp = tmp + '0';
		} else {
			tmp = tmp + '1';
		}
	}
			var sub=[];
			sub[0] = format(tmp.substr(0,8),'0',8);
			sub[1] = format(tmp.substr(8,8),'0',8);
			sub[2] = format(tmp.substr(16,8),'0',8);
			sub[3] = format(tmp.substr(24,8),'0',8);

			var ip=[];
	for(i=0;i<4;i++) {
			ip[i] = parseInt(nthf(ipdis,'.',i+1),10);
			ip[i] = format(ip[i].toString(2),'0',8);
	}

			var net=[];
	for(i=0;i<4;i++) {
			net[i]='';
	for(var j=0;j<8;j++) {
		if (sub[i].substr(j,1)=='1' && ip[i].substr(j,1)=='1'){
			net[i] = net[i] + '1';
		} else {
			net[i] = net[i] + '0';
		}
	}
	}

			var host = 32-parseInt(ipsub,10);
			tmp = '';
	for(i=0; i<8*4; i++) {
		if (i>32-host-1){
			tmp = tmp + '1';
		} else {
			tmp = tmp + '0';
		}
	}
			var n_hos=[];
			n_hos[0] = tmp.substr(0,8);
			n_hos[1] = tmp.substr(8,8);
			n_hos[2] = tmp.substr(16,8);
			n_hos[3] = tmp.substr(24,8);

			var brod=[];
	for(i=0;i<4;i++) {
			brod[i] = '';
	for(j=0;j<8;j++) {
		if (net[i].substr(j,1)=='1' || n_hos[i].substr(j,1)=='1'){
			brod[i] = brod[i] + '1';
		} else {
			brod[i] = brod[i] + '0';
		}
	}
	}
			var ip_range = parseInt(net[0],2)+'.'+parseInt(net[1],2)+'.'+parseInt(net[2],2)+'.'+parseInt(net[3],2)+' ~ '
			ip_range += parseInt(brod[0],2)+'.'+parseInt(brod[1],2)+'.'+parseInt(brod[2],2)+'.'+parseInt(brod[3],2);

	document.getElementById('debug').innerHTML = sub[0]+'.'+sub[1]+'.'+sub[2]+'.'+sub[3]+' サブネット<br />';
	document.getElementById('debug').innerHTML += ip[0]+'.'+ip[1]+'.'+ip[2]+'.'+ip[3]+' IP<br />';
	document.getElementById('debug').innerHTML += net[0]+'.'+net[1]+'.'+net[2]+'.'+net[3]+' AND→ネットワークアドレス<br />';
	document.getElementById('debug').innerHTML += n_hos[0]+'.'+n_hos[1]+'.'+n_hos[2]+'.'+n_hos[3]+' サブネットマスクの反転=ホストアドレスの反転<br />';
	document.getElementById('debug').innerHTML += brod[0]+'.'+brod[1]+'.'+brod[2]+'.'+brod[3]+' IPとOR→ブロードキャストアドレス<br />';

	document.getElementById('debug').innerHTML += 'IP範囲(ネットワークアドレス~ブロードキャストアドレス):';
	document.getElementById('debug').innerHTML += ip_range;

	if(navigator.clipboard){
		navigator.clipboard.writeText(ip_range);
	}

IP範囲計算に使用する自作関数

上記内で使用した3つの自作関数。

function format(dt,letter,num) {
	//dt:文字列、letter:桁合わせに使う文字、num:欲しい文字数
		var r=dt;
	for(var i=0;i<num;i++) {
		if (r.length==num){
				break;
		} else {
				r = letter + r;
		}
	}
				return r;
}

function nthf(dt,spr,n) {
		var tmp;
		var rt='';
	if (dt.indexOf(spr) == -1){
				rt=dt;
	} else {
				tmp=dt.split(spr);
		if (n<1||n>tmp.length){
				rt='';
		} else {
				rt=tmp[n-1];
		}
	}
				return rt;
}

function replaceall(dt,a,b) {
		var tmp;
		var r='';
	if (dt.indexOf(a) == -1){
	//引数aが存在しない時は引数dtをそのまま返す。
				r=dt;
	} else {
				tmp=dt.split(a);
	        for (var i=0;i<tmp.length;i++){
			if (i==0){
				r=tmp[i];
			} else {
				r=r+b+tmp[i];
			}
		}
	}
				return r;
}

ついでに.htaccessでのIP表記

上記でIP範囲を調べた上、正規表現で.htaccessに指定することに。ついでなので、正規表現化する際の例とか注意点とか。

ピリオド=任意の1文字と規定される正規表現故の分かりづらい点です。(エスケープ文字\の扱い)

IP正規表現化の注意点
1 IP3つ目までの最後が2桁以下 \.で締めくくり。3桁なら不要。
0~99指定も2桁以下なので[0-9]?[0-9]\.としないと。
2 IP4つ目まで指定&4つ目が2桁以下 49\.12\.11\.11$と最後は$で締めくくる
	SetEnvIf Remote_Addr "^(5\.9\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(5\.78\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(5\.161)" ip=yoursrv
	SetEnvIf Remote_Addr "^(78\.46\.7[2-5]\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(78\.47\.8[0-3]\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(88\.99\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(88\.198)" ip=yoursrv
	SetEnvIf Remote_Addr "^(95\.21[6-7])" ip=yoursrv
	SetEnvIf Remote_Addr "^(135.181)" ip=yoursrv
	SetEnvIf Remote_Addr "^(157.90\.[0-9]?[0-9]\.|157.90\.1[0-9][0-9]|157.90\.20[0-8])" ip=yoursrv
	SetEnvIf Remote_Addr "^(157.90\.2[1-5][0-9])" ip=yoursrv
	SetEnvIf Remote_Addr "^(157.90\.209.[0-6]?[0-9]$|157.90\.209.7[0-6]$|157.90\.209.7[8-9]$)" ip=yoursrv
	SetEnvIf Remote_Addr "^(157.90\.209.[8-9][0-9]$|157.90\.209.[1-2][0-9][0-9])" ip=yoursrv
	SetEnvIf Remote_Addr "^(162.55\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(188.40\.)" ip=yoursrv
	SetEnvIf Remote_Addr "^(195.201)" ip=yoursrv

backup.tar.gzなんてものにアクセスしてくるスパムボット。そのターゲットディレクティブだけを条件に5GBのゴミデータに転送するのはちょと気が引けたり。

間違いなくウジ虫を狙い撃ちできるよう、IP範囲の調査・.htaccessでの指定を確実に行う必要があります。落ち着いてマスターしてみてどうぞ。

コメント

タイトルとURLをコピーしました