r := net.Resolver{}
ip, err = r.LookupHost(context.Background(), "google.com")
みたいな感じで名前解決をしようとするとAとAAAAの2回クエリが飛ぶので追ってみた。最初は以下が呼ばれる。読むのは2ファイルのみsrc/net/dnsclient_unix.go
func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) { return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS) }
次に以下が呼ばれる。具体的な名前解決のするのは次のgoLookupIPCNAMEOrder。ポイントは呼び出す際の第二引数がハードコードされているという点。
func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) { if order == hostLookupFilesDNS || order == hostLookupFiles { // Use entries from /etc/hosts if they match. addrs = lookupStaticHost(name) if len(addrs) > 0 || order == hostLookupFiles { return } } ips, _, err := r.goLookupIPCNAMEOrder(ctx, "ip", name, order) // ここで文字列"ip"が固定されている if err != nil { return } addrs = make([]string, 0, len(ips)) for _, ip := range ips { addrs = append(addrs, ip.String()) } return }
qtypesっていうのがクエリのタイプになるのだがデフォルトだとAとAAAAが入っていて第二引数で渡されたnetworkの値が
func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) { qtypes := []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA} switch ipVersion(network) { case '4': qtypes = []dnsmessage.Type{dnsmessage.TypeA} case '6': qtypes = []dnsmessage.Type{dnsmessage.TypeAAAA} }
src/net/lookup.go
のipVersionを見ると末尾の数字を見てIPv4なのかIPv6なのかを見ている。LookupHost経由でこの関数を呼び出す際は常にipが渡されるのでn = 0が戻る。0の場合はqtypes := []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
から変わらないので常に2回クエリが飛ぶという。
func ipVersion(network string) byte { if network == "" { return 0 } n := network[len(network)-1] if n != '4' && n != '6' { n = 0 } return n }
CGO_ENABLED=1とかも試したが変わらなかった。