DNSサーバーとお喋りしてみた
そういえばDNSサーバーとお喋りしたこと無いな、やり方知らないな、と思ったので土日でお喋りしてみました。 この記事は技術記事ではありません。感想日記です。
技術目的で来てしまった方のために先に参考にした資料をまとめます。私の書いてる本文はただの感想文です。
参考にした資料
あとChat-GPTにも色々聞きました
感想
コードです
全体
土日でざっくり実装できるぐらいにはシンプルな印象でした。 面倒くさくてdomain文字列の最後にポインターが来るパターンのドメイン文字列の読み取り実装はサボりました。
お試しだったので思いっきり環境依存なコードになっています。WinSockとVisual Studio 2022 17.10で書きました。 あとからポータブルに書き直したくなってきましたが、動かすことを優先しました。
C++
C++のキャッチアップできてないので古い書き方が多いかもしれません。特にIOまわりはformatやprintlnできたので古そう。 もっとconstやconstexprにできる箇所があるかもしれません。レガシーなキャストを使ってる箇所があるかもしれません。
バイナリを順番に移動して読み取りをする操作を書いてて思ったのですが、C++にメモリバイナリストリームがほしい!と思いました。ありそうでない。
バイナリ読み取りは std::ifstream
ありますが、これはメモリを読み取るものではないです。
とりあえず今回は std::vector<std::byte>
として扱いましたが、現在のC++ではこれが無難なんですかね?
あとrecv/sendをラップした通信のiostreamもあったらいいのになと思いました。
標準にAsio入れようとする動きがあるのでその辺落ち着いたらですかね。
名前なし構造体が非標準なので怒られていますが、透過的にメンバアクセスできるのが便利で直したくない 😅
コードの前半は型定義やOStremのオーバーロードでコード量がかさんでいます。
DNS
一部セキュリティまわりや、DNSの込み入った仕様の部分はわからないところもありましたが、基本的なヘッダや各Record部分はわかりやすいと思いました。 規格を探したときに、RFC1035自体をアップデートするのではなくパッチ的に別の規格で機能を追加するんだ~と知りました(なぜ?) パケットをTCPではなくUDPで送っていることも、1パケットで収まるように512バイトに収まるようにしているなど、初めて知りました。 パケットの長さを抑えるためにドメイン名をポインタ(オフセット)で参照できる方法は面白かったです。
IDを乱数にしたいと思っていたのに定数にしたまま忘れてました…
その他
パケットとメモリのエンディアンの違いを忘れてて、しばらく htons
と stohs
忘れてて詰まったりしてました。使わないと忘れますね。
また、 dns_header
のビットフィールドのフラグの定義順を規格の順番通りに定義したかったのですが、どうしてもリクエストやレスポンスのメモリのマッピングが上手くいかず諦めて定義順を変えました。くやしいです。