勘違いしながらドはまりしながら作り上げていくジジイの『JavaScript奮闘記』です。今回はムカつくスパムボットを懲らしめるために必要になるIP範囲の計算ツールを作ります。
ポク太郎です。
よく分からんネットワークの決まり。
IP範囲の計算を理解するため、JavaScriptで作成し、その計算方法を確認・理解します。
キーワードは“サブネットマスク、ネットワークアドレス、ホストアドレス、ブロードキャストアドレスの計算”て感じ。
出来上がったツールはこちら。
IP範囲を求める計算方法
詳細はJavaScriptのコードを追いかけるとして、最初にザザッと大まかに書くと、
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進数変換関数を用いるとなおさら訳が分からなく→ここでは0と1で文字列を作り、IF文にて1文字づつ比較することでAND、OR論理を演算するようにしました。
プログラムが読める方は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つの自作関数。
- format(dt,letter,num)…文字列の桁数を合わせる。
- nthf(dt,spr,n)…指定文字で区切られた指定番号のデータを返す。
- replaceall(dt,a,b)…指定の文字を全部入れ替える。
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文字と規定される正規表現故の分かりづらい点です。(エスケープ文字\の扱い)
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での指定を確実に行う必要があります。落ち着いてマスターしてみてどうぞ。
コメント