突破口(後編)

初めに断っておかなければならないのは、今回紹介する方法は前回挙げた2つ目の大きな問題の解決…いや、代替案というか、とにかく、今回の方法では前回挙げた問題はある程度回避できるのだが、まだ幾つかの問題が残っており、その中にはDNS割り込みに100%成功できないといった手のものも含まれている。まあ、それで金とろうってワケじゃあないし、後々検討するということで勘弁してくれ。あと、いまだ検証が不十分なので、いざやってみると思わぬ障害にぶち当たる可能性ってのはもちろんあるとも。(ほら、パケット投げすぎでルータに拒否られるとか、FireWallにブロックされるとか…)
前置きが長くなったが、はじめよう。前回、ARPの偽装応答にて、対象端末のDNSサーバ(ルータ)のMACをブロードキャストにすりかえる荒業を検討した。コレにより必要に応じてDNSの偽装応答を返せる状態に持ち込んだが、その一方で、ディフォルトゲートウェイをつぶしてしまうという副作用により、インターネットに接続ができないという問題が発生した。さて、この問題はどうやって解決したものか?
1. MACがブロードキャストである以上、パケットはセグメントを越えられず、インターネットに接続が出来ない。(ディフォルトゲートウェイをつぶしてしまう問題)
2.MACがブロードキャストでない限り、DNS応答を正確に返すことは出来ない。
一方を取ればもう一方が…、ふーむ。
実のところ、この方法を採用する限り、1と2を両立させるという解答はありえない*1。であれば、1と2の状態をそのときの状況に応じ、使い分けさえすればよいのだ。
1) 対象端末のDNSサーバMACをブロードキャストにすりかえる。(状態1)
2) DNS要求を受信したら内容を確認し、必要に応じてDNS応答を返す。
3) HTTP要求を受信したらDNSサーバMACを元のMACに戻す。(状態2)
4) 一定時間経過後、1)へ
オーケイ。何だコレは、ってんだろ。確かに、これは全然完璧じゃあない解答だ。まあ、それゆえに、散々前置きを書いたんだが、まあ、一寸説明させてくれ。この解答の問題のある場所は主に3〜4。この間で新たにDNS要求が発生した場合、DNS割り込みはできない。そして、3でHTTPを受信してからARPを元に戻したってもう通信は失敗しているのだから遅いだろう。ってところだろうか?
まず簡単な後者からだが、実のところ、これは決して遅くない。HTTPの通信に失敗した場合、自動的にリトライが働くからだ。そして、ここが重要なんだが、このリトライ、タイムアウトまで「数秒間の」猶予がある。
では次に前者の問題。残念だが、コレを避ける方法はない。つまり、この隙にDNS要求が出されれば、どうやったってそれを捕捉は出来ない。そして、いつまで続くか判らないHTTPの通信のため(DNS(ゲートウェイ)のMACがブロードキャストじゃあないので要求が完了したことを知る方法がない)、いつまでスリープすればいいのかも判らないのだ。スリープが短すぎればHTTP要求は失敗し、長過ぎればDNS要求を見逃す可能性が増大する。しかし、こう考えたらどうだろう。
「状態2」を維持するスリープ時間は「状態1」でHTTPパケットを受信してから「状態2」へ戻すまでのスリープ時間と同等か、それ以下とする。
この条件の追加により、DNS割り込みの成功率は理論上50%を超えることになる(状態1の維持時間 = MACすり替え後〜HTTP要求受信 + スリープ時間 であるため)。『でも、それじゃあ、例えば、重たいHPを開いたときとか、ページを開くのに失敗してしまうのでは…?』 もっともな疑問だ。そして、そう、そのような「読み込みに時間のかかるページについては」読み込みを一旦失敗させてしまえばいい。HTTPの通信に失敗するということは、要求していたページは正しく表示されないということ。もしかすると画像の一部が欠けただけかもしれないし、また、もしかするとページ自体真っ白になってしまい、何も読めないかもしれない。では、もし、Webブラウジング中にこんな状況に出くわしたとき、ユーザはどうするか? 簡単だ、ページを再度読み込むだろう。例えば画像の一部が欠けたような状態なら、再度ページを読み込んだときには、他の部分はブラウザのキャッシュから読み込まれる。だから、以前より高速にページの読み込みが行なえだろうし、コレだけで問題が解決されるかもしれない。そして、コレがアイディアのコアの部分だが、ユーザが再度読み込みを試みた場合、スリープ時間を一時的に延ばすのだ。
初めにユーザがページを読み込んだ際、ステップ2にて我々は既に対象端末から一度HTTPの要求パケットを受信している。コレを利用する。すなわち、HTTPの要求パケットの受信時、前回受信したHTTP要求パケットと比較し、もしコレが同一の内容であった場合、ユーザが再度読み込みを行なったと判定するのである。もちろん、もしユーザがYoutubeのようなサイトにアクセスしていた場合、読み込みにかかる時間は計り知れない。同一の要求が繰り返されるたびにスリープ時間を伸ばしていく必要がでてくるだろう。しかし、最終的にページにつながりさえすれば、あわよくば、「今日はネット調子悪いなあ」程度にしか感じられないだろう…。
長くなったが最後に、一寸話を戻して、この方法の成功率に関して、もう少しだけ追加で説明をしよう。状態2の維持時間(スリープ時間)を仮に1.5秒、HTTP要求受信から状態2へ切り替える時間(スリープ時間)を仮に1.5秒とする。貴方はどこかのページを開こうとして1.5秒のラグの後にページが開かれた。さて、貴方は最大1.5秒以内に別のページを開くだろうか…? こう考えると、理論上、DNS割り込みの成功が保証されているのは50%であっても、実際にはそれよりずっと高い確率で割り込みに成功するのではないかと期待できる、そう思わないか?
さて、次回は散々長ったらしく書いてきた手法について簡単なまとめと、応用について書く、かも知れない。

*1:代替DNSサーバが指定されている場合は例外となるが、一般のご家庭でそんなことってあるのだろうか…?