はじめに
同僚の@ryoma123さんが作っているGo製DNS CLIツール dic を業務で日常的に使っています。
dicは複数のDNSサーバーにまとめて問い合わせし、結果を一覧表示してくれるツールです。DNS切り替え前の確認やCDNの挙動確認でよく使っています。goroutineによる並行クエリで結果が速く返ってくるのも気に入っているポイントです。
使っているうちに「逆引きとCNAME追従もあったら便利だな」と思ったので、Claude Codeを活用してPRを出しました。その実践記録です。
追加した機能:
- 逆引き(PTR)とCNAMEの追従機能
- Homebrew Tapによる配布の準備(goreleaser設定・GitHub Actionsワークフロー)
dicの良いところ
機能追加の話の前に、dicの設計で良いと思っている点を紹介します。
- シンプルなインターフェース — ドメインを渡すだけで複数DNSサーバーの結果が一覧で見える
- goroutineによる並行処理 — クエリが並行実行されるのでレスポンスが速い
- 設定ファイルで問い合わせ先を管理 — TOML形式でDNSサーバーリストを定義できる
- 22ファイル・約800行のコンパクトな設計 — 機能がシンプルに整理されていて読みやすい
この設計のおかげで、機能追加もスムーズにできました。
Claude Codeで既存プロジェクトに貢献する
コード構造の把握
dicのコードベースをClaude Codeに渡すと、cli.go → dic.go → dns.go → line.go の依存関係をすぐに把握し、全体のデータフローを理解してくれました。
コードが整理されているおかげで、Claude Codeも構造を正確に読み取れていました。
既存パターンを活かした実装
元のコードでは wg.Add(1) + goroutine + wg.Wait() でDNSクエリを並行実行しています。Claude Codeはこのパターンを検出し、新機能でも同じ並行処理パターンを踏襲しました。
新たに追加した appendAnswers 関数では、goroutineからの結果書き込みに sync.Mutex による排他制御を加えています。
func appendAnswers(ls *lines, mu *sync.Mutex, l line,
answers []dns.RR, qtype string, qIndex int) []string {
var addrs []string
for _, a := range answers {
l.answer = a
l.qtype = qtype
l.qIndex = qIndex
mu.Lock()
*ls = append(*ls, l)
mu.Unlock()
// ...
}
return addrs
}
既存の変数命名や構造体の使い方を維持しつつ、新機能を追加できています。
テストコードの追加
逆引きアドレスの変換やオプションのパースなど、テストしやすい部分のテストコードも追加しました。
func TestReverseAddr(t *testing.T) {
r, ok := reverseAddr("1.2.3.4")
if !ok {
t.Fatalf("Expected reverseAddr to succeed")
}
if e := "4.3.2.1.in-addr.arpa."; r != e {
t.Errorf("Expected %q, got %q", e, r)
}
}
ネットワーク非依存のテストなので、CIでも安定して回せます。
実装詳細 — 逆引き・CNAME追従の仕組み
逆引き(PTR)対応
--reverse / -r フラグを追加しました。引数にIPアドレスを渡すと、PTRレコードを問い合わせます。
# IPアドレスの逆引き
$ dic -r 8.8.8.8
# ドメイン引数はそのまま正引きされる
$ dic -r 8.8.8.8 example.com
実装のポイントは、引数がIPアドレスかドメイン名かを判定し、IPアドレスの場合のみPTRクエリに切り替える部分です。
func isIPAddress(s string) bool {
return net.ParseIP(s) != nil
}
dns.ReverseAddr が 8.8.8.8 を 8.8.8.8.in-addr.arpa. に変換してくれるので、あとはPTRクエリを投げるだけです。
CNAME追従
--follow-cname / -f フラグで、CNAMEレコードが返ってきた場合にA/AAAAレコードまで自動的に辿ります。
# CNAMEの先のA/AAAAレコードまで追従
$ dic -f www.example.com
# 逆引きと組み合わせることも可能
$ dic -f -r www.example.com
CNAMEチェーンには深さ制限があり、--cname-max / -m で最大追従数を指定できます(デフォルト5)。--reverse と組み合わせた場合は、CNAME追従で得られたA/AAAAレコードのIPに対してさらにPTR逆引きを実行します。
設定ファイルパスの指定
--config / -c で設定ファイルのパスを指定できるようにしました。プロジェクトごとに異なるDNSサーバーリストを使いたいときに便利です。
$ dic -c ./config.toml -r 8.8.8.8
Homebrew Tap配布の自動化
せっかく便利なツールなので、もっと多くの人に使ってもらいたいと思い、brew install でインストールできるようにする準備もPRに含めました。Tapリポジトリの作成やトークンの設定はdic作者側で行う必要がありますが、goreleaser設定とGitHub Actionsワークフローを用意しています。
goreleaserの設定
既存の .goreleaser.yml をv2形式に更新し、brews セクションを追加しました。
version: 2
builds:
- main: ./cmd/dic
env:
- CGO_ENABLED=0
goos:
- windows
- darwin
- linux
goarch:
- amd64
- arm64 # Apple Silicon対応を追加
brews:
- repository:
owner: ryoma123
name: homebrew-tap
homepage: https://github.com/ryoma123/dic
description: CLI tool for collecting domain information from multiple DNS servers
license: MIT
install: |
bin.install "dic"
test: |
system "#{bin}/dic", "--version"
arm64を追加してApple Siliconにも対応しています。
GitHub Actionsワークフロー
タグをpushしたら自動でリリースが走るワークフローを追加しました。
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- uses: goreleaser/goreleaser-action@v6
with:
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
これで以下のコマンドだけでリリースとHomebrew formulaの更新が完了します。
git tag v1.0.0
git push origin v1.0.0
ユーザーは brew tap ryoma123/tap && brew install dic でインストールできます。
貢献する際に気をつけたこと
後方互換性の担保
新しいフラグはすべてオプショナルで、既存の使い方に影響しません。Options 構造体のゼロ値(Reverse: false, FollowCNAME: false)が従来の動作になるよう設計しています。既存ユーザーの使い方を壊さないように気をつけました。
PRの分割
機能追加(逆引き・CNAME追従)とHomebrew対応は別のPRにしました。レビューしやすさと、問題が起きたときの切り分けのためです。
Claude Codeの出力を鵜呑みにしない
Claude Codeは便利ですが、意図しない変更が混ざることもあります。既存コードへのリスペクトを忘れず、差分レビューは丁寧に行いました。
おわりに
dicは日常のDNS調査で手放せないツールになっています。今回、逆引き・CNAME追従とHomebrew配布を追加させてもらったことで、さらに使いやすくなりました。
Claude Codeは既存プロジェクトへの貢献でも力を発揮します。設計がしっかりしたコードベースであれば、既存パターンを正確に読み取って踏襲した実装を生成してくれるので、元の設計パターンを壊さずに機能追加できます。
もっと使われたら嬉しいです。

