前置き
こんにちは、柊 菜緒です。
「エンジニア集会」アドベントカレンダーの2日目を担当します。
皆さん2025年も師走(しわす)に入りましたがいかがお過ごしでしょうか?
私は先月この記事を書き始めた直後にHDDが吹っ飛び、ストレスで今日までに1~2kg痩せました。
書き始めた直後だったのでこの記事の元になるデータはテキストエディタで開いていてメモリー上に展開されていたのでなんとか助かりました。
大事なデータはしっかりバックアップを取ろう。
良いですね?バックアップは取りましょう。
始まりは突然に
ある日韓国の友人にこんな事を相談されました。
「最近寮で24時以降になるとグラブルも含めてゲームに繋がらなくなる」
「???……いったい何がどういう事だ」
「寮で24時以降ゲームできなくなるようするシステムが導入されてて、この時間になるとさっき言ったとおりにグラブル含めてオンラインゲームのWebサイトも開けなくなる」
「ふむ、どんな感じの表示になる?」
「ページを開こうとしてもサーバーが見つかりませんって表示される」
システムの仕組みは如何に?
「聞いた感じだとIPフィルタリングかDNSフィルタリングしてるのかねぇ」
ちょうど直近で中国のグレートファイアウォールの技術に関する記事を読んでいてその知識から推測。
「nslookupでゲーム関係のドメインのIPアドレスを正引きできる?」
「できないね」
「じゃあDNSフィルタリングの可能性高そうだな」
「試しにWindowsのプライマリDNSを8.8.8.8に設定して試してみて」
8.8.8.8はGoogleが提供しているPublic DNSのアドレスです。
「ダメだね」
「うーむ、DNSのUDPパケットレベルで書き換えてるのかな(お行儀が悪いシステムだなぁ)」
DNSは通常、暗号化されていない平文のUDPパケットで通信するため、経路上での書き換えが比較的容易です。
「試しにうちのVPNをゲートウェイとしてDNSにアクセスしてみて」
自宅ルーター(YAMAHA RTX3000)にL2TP/IPsec機能があったので、通信経路を丸ごと暗号化してテストしました。
「お、上手く通ったね」
「やっぱり経路上で強制的にDNSパケットレベルで書き換えてるっぽいな(気に食わないなぁ)」
「うちのVPN経由で通信したら確実なんだろうけど……うちの回線速度がなぁ」
「なおくんに迷惑かけるわけにはいかないからあきらめるわ」
当時の私の家の回線がADSLだったため、友人の通信全てを引き受けるには回線が細すぎました。(DL8M~10Mbps・UP800K~1Mbps)
(ふと疑問が浮かんだ。そういえば何で韓国の寮のシステムで日本のグラブルまでカバーしてるんだろ……
ん?待てよ確かグラブルのドメインってgame.granbluefantasy.jpだったよな?いやいやまさか……)
「ちょっとゲーマーズ(www.gamers.co.jp)開けるか試してみて」
「開けないね」
(カッチーン、これドメイン名にgameって入ってたら無差別にDNSパケット書き換える行儀がわりぃクソシステムに違いねぇ!)
(深夜のテンションで私の謎のやる気スイッチが入った瞬間である)
けしからん駆動開発、開始
「通信を改変できなくすればいいからちょっと他の方法調べてみるわ」
「DNS通信の暗号化か……そういえば最近GoogleがDNS over HTTPSとか言うのを独自実装で実験的に提供し始めたって記事を見たような気がするな、調べてみるか。」
当時見た記事
https://eng-blog.iij.ad.jp/archives/85
「ふむふむ、HTTPSのGETメソットのクエリストリングでパラメータを投げるとJSONで返ってくるんだな」
「ちょうど最近RPGツクールMVでJavaScript触ってたばっかりだから勉強がてらNode.jsで回避できるアプリ作ってみる」
「別にそこまでしなくていいよ?」
「私は何か求められないとやる気が起きないから私の勉強に付き合うと思って……ね?」
「おう」
いざ実装!Node.jsで爆速プロトタイピング
さて、とりあえずブラウザからクエリ送って応答のフォーマットを見てみようかな?
ふむ、こんな感じで返ってくるのか。
DNSのサーバー部分まで作るのは時間がかかるな……良いライブラリ無いかな?
dnsdって言うのがあるな。
じゃあこれで受け取ったDNSクエリをHTTPSの形にしてその応答をまたDNSクエリの応答として返せばいいな。
でもこのままじゃ毎回HTTPSで取って来たらオーバーヘッドも大きそうだし、JavaScriptのオブジェクトを連想配列として使って有効期限無限の簡単なキャッシュモドキも作っておくか。
どうせアプリ再起動したらメモリーから消えるし使う時だけ起動してもらえばキャッシュも消えるだろうし問題ないだろう。
あとは、たぶんプライマリに通常のGooglePublicDNS設定してセカンダリにこいつ登録しておけばフィルタリングされないドメインはそのままプライマリで引かれるはず(あまりよろしくないやり方)
「とりあえずできたから使ってみて~」
「使い方はプライマリDNSに8.8.8.8設定してセカンダリDNSに127.0.0.1を設定したら使えるはず」
127.0.0.1はループバックアドレス(いわゆるlocalhost)で、自分自身のPCを指します。
「早っ、サンキュとりあえず使って見るわ」
「お、行けた」
「あー、やっぱりDNSをパケットレベルで書き換えてたか~」
「それPCのファイヤーウォール設定して公開したらPCのローカルIPアドレス指定してやればLAN内からスマホとかでも使えるよ」
ゲートウェイ上で書き換えを行っていたので、ローカルエリアネットワーク内(PCとスマホ間など)で平文のDNSパケットをやり取りしても、書き換えは発生しないと判断しました。
「お、そうなんか」
こうして友人は夜も寮でゲームをできるようになった。
そして伝説へ…
そして約1年経った今日その友人含む数名とJavaScriptの話をしていた時。
「前に作ったあれもJavaScriptで作ったんだよね~、そういえばあれからどうなった?」
「あ~、あれね?ぶち抜いてたのバレてフィルタリングシステム自体使われなくなった。」
「管理者にハッタリかまして、またどうせ抜け道作りますよ?って」
「100万円超えるシステムにまた金払いますか?それやるならキャンペーンとかルームメイトからの通報もらって対応する方が経済的だと思います。と意見したら通ったって」
「え?みんなに配ったりしたの?」
「あのシステム夜中にゲームする人たちで寝れねぇってクレームから導入したらしいから。配ってはないけど、メインサーバーにぶち込んだ」
「メインに突っ込んだからすぐにばれれた。ネット会社の人が点検に来ました → 建物の使ってるDNSがすべてパブリックDNSですよって話 → メイン確認します → あれ、これなんだ……」
オチ
MXレコードにも対応させたけどよく考えたらメールサーバー公開して無ければMXレコードって必要なくね……(・ω・)
ソースコード
これがその時作ったNode.jsアプリです。(多少書き換えた気もするしエイヤって作っただけだからもう動かないはずだけど)
https://github.com/n2naokun/Node-JS/blob/master/Files/DnsOverHTTPStoLocalDnsServer.js
最後に
この記事には経緯などうろ覚えな内容が含まれます。