[インデックス 13398] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるDNSルックアップのバグ修正に関するものです。具体的には、Cgoを使用しない環境で、AAAAレコード(IPv6アドレス)のみを持つホスト名の解決が正しく行われない問題を修正しています。
コミット
commit dfbd42e4f881717a58309d59e40671a29572dd12
Author: Michael Stapelberg <michael@stapelberg.de>
Date: Mon Jun 25 17:32:39 2012 -0400
net: fix lookup of AAAA-only hosts without cgo
Fixes #3762.
R=rsc
CC=golang-dev
https://golang.org/cl/6303106
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dfbd42e4f881717a58309d59e40671a29572dd12
元コミット内容
net: fix lookup of AAAA-only hosts without cgo
変更の背景
このコミットは、Go言語の標準ライブラリであるnet
パッケージにおけるDNSルックアップの不具合を修正するために行われました。具体的には、GoのネイティブDNSリゾルバ(Cgoを使用しない場合)が、Aレコード(IPv4アドレス)を持たず、AAAAレコード(IPv6アドレス)のみを持つホスト名を正しく解決できないという問題(Issue 3762)に対応しています。
従来のgoLookupIP
関数では、まずAレコードのルックアップを試み、その後にAAAAレコードのルックアップを行っていました。もしAレコードのルックアップが失敗した場合、たとえAAAAレコードのルックアップが成功しても、全体としてエラーが返されてしまい、AAAAレコードのみを持つホスト名が解決できないという問題がありました。これは、IPv6のみの環境や、IPv6を優先する設定のシステムにおいて、ネットワーク接続に支障をきたす可能性がありました。
前提知識の解説
DNS (Domain Name System)
DNSは、インターネット上のドメイン名(例: example.com
)をIPアドレス(例: 192.0.2.1
や2001:0db8::1
)に変換するための分散型データベースシステムです。ユーザーがWebサイトにアクセスする際、ブラウザはまずDNSを使ってドメイン名に対応するIPアドレスを調べ、そのIPアドレスを使ってサーバーに接続します。
AレコードとAAAAレコード
DNSには様々な種類のリソースレコードがありますが、IPアドレスに関連する主要なものとしてAレコードとAAAAレコードがあります。
- Aレコード (Address Record): ドメイン名に対応するIPv4アドレスを定義します。例えば、
example.com
が192.0.2.1
というIPv4アドレスを持つ場合、Aレコードとして登録されます。 - AAAAレコード (Quad-A Record): ドメイン名に対応するIPv6アドレスを定義します。例えば、
example.com
が2001:0db8::1
というIPv6アドレスを持つ場合、AAAAレコードとして登録されます。
現代のインターネットではIPv4とIPv6が共存しており、多くのホストは両方のアドレスを持つか、あるいはどちらか一方のみを持つ場合があります。
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのnet
パッケージは、DNSルックアップにおいて、Cgoを使用してシステムのリゾルバライブラリ(例: glibcのgetaddrinfo
)を呼び出すことができます。しかし、Cgoを使用しないビルド(CGO_ENABLED=0
)の場合、Goは独自のネイティブDNSリゾルバを使用します。このネイティブリゾルバは、Cgoに依存しないためクロスコンパイルが容易であるなどの利点がありますが、システムのリゾルバとは異なる動作をする場合があります。今回の問題は、このネイティブリゾルバの挙動に起因していました。
技術的詳細
修正前のgoLookupIP
関数は、ホスト名name
に対してIPアドレスをルックアップする際に、以下の順序で処理を行っていました。
lookup(name, dnsTypeA)
を呼び出してAレコード(IPv4)を検索。- Aレコードが見つかった場合、それらを
addrs
に追加。 lookup(name, dnsTypeAAAA)
を呼び出してAAAAレコード(IPv6)を検索。- AAAAレコードが見つかった場合、それらを
addrs
に追加。
問題は、Aレコードのルックアップ(err4
に対応)でエラーが発生した場合の処理にありました。修正前は、err4
がnil
でない場合、即座にそのエラーを返していました。
// 修正前のコードの抜粋
cname, records, err = lookup(name, dnsTypeA)
if err != nil {
return // Aレコードのルックアップでエラーがあれば、ここで終了
}
// ...
_, records, err = lookup(name, dnsTypeAAAA)
if err != nil && len(addrs) > 0 {
// Aレコードが取得できていれば、AAAAのエラーは無視
err = nil
}
if err != nil {
return // AAAAのエラーがあれば、ここで終了
}
このロジックでは、もし対象のホストがAレコードを持たず、AAAAレコードのみを持つ場合、lookup(name, dnsTypeA)
がエラーを返します。すると、そのエラーが即座にgoLookupIP
から返されてしまい、その後のAAAAレコードのルックアップが実行されませんでした。結果として、AAAAレコードのみを持つホスト名が解決できないという問題が発生していました。
コアとなるコードの変更箇所
変更はsrc/pkg/net/dnsclient_unix.go
ファイルのgoLookupIP
関数に集中しています。
--- a/src/pkg/net/dnsclient_unix.go
+++ b/src/pkg/net/dnsclient_unix.go
@@ -237,24 +237,30 @@ func goLookupIP(name string) (addrs []IP, err error) {
}
var records []dnsRR
var cname string
- cname, records, err = lookup(name, dnsTypeA)
- if err != nil {
- return
- }
+ var err4, err6 error
+ cname, records, err4 = lookup(name, dnsTypeA)
addrs = convertRR_A(records)
if cname != "" {
name = cname
}
- _, records, err = lookup(name, dnsTypeAAAA)
- if err != nil && len(addrs) > 0 {
- // Ignore error because A lookup succeeded.
- err = nil
+ _, records, err6 = lookup(name, dnsTypeAAAA)
+ if err4 != nil && err6 == nil {
+ // Ignore A error because AAAA lookup succeeded.
+ err4 = nil
}
- if err != nil {
- return
+ if err6 != nil && len(addrs) > 0 {
+ // Ignore AAAA error because A lookup succeeded.
+ err6 = nil
}
- addrs = append(addrs, convertRR_AAAA(records)...)
- return
+ if err4 != nil {
+ return nil, err4
+ }
+ if err6 != nil {
+ return nil, err6
+ }
+
+ addrs = append(addrs, convertRR_AAAA(records)...)
+ return addrs, nil
}
コアとなるコードの解説
修正後のgoLookupIP
関数では、AレコードとAAAAレコードのルックアップ結果のエラーハンドリングが改善されています。
-
エラー変数の分離:
- Aレコードのルックアップ結果のエラーを
err4
に、AAAAレコードのルックアップ結果のエラーをerr6
にそれぞれ格納するように変更されました。これにより、両方のルックアップを独立して実行し、後でエラーを評価できるようになります。
- Aレコードのルックアップ結果のエラーを
-
エラー評価ロジックの変更:
if err4 != nil && err6 == nil
: Aレコードのルックアップが失敗し、かつAAAAレコードのルックアップが成功した場合、Aレコードのエラー(err4
)は無視されます。これは、AAAAのみを持つホストの場合に、AAAAレコードが取得できたのであれば、Aレコードの失敗は問題ではないという判断です。if err6 != nil && len(addrs) > 0
: AAAAレコードのルックアップが失敗し、かつAレコードのルックアップが成功してaddrs
にIPアドレスが追加されている場合、AAAAレコードのエラー(err6
)は無視されます。これは、Aレコードが取得できたのであれば、AAAAレコードの失敗は問題ではないという判断です。
-
最終的なエラーチェック:
- 上記の条件でエラーが無視されなかった場合、つまり、AレコードとAAAAレコードの両方のルックアップが失敗し、かつどちらか一方でも成功しなかった場合にのみ、エラーが返されるようになりました。
if err4 != nil { return nil, err4 }
if err6 != nil { return nil, err6 }
この変更により、Aレコードのルックアップが失敗しても、AAAAレコードのルックアップが成功すれば、そのAAAAレコードが結果として返されるようになり、AAAAレコードのみを持つホスト名の解決が可能になりました。
関連リンク
- Go Issue 3762: https://github.com/golang/go/issues/3762
- Go CL 6303106: https://golang.org/cl/6303106
参考にした情報源リンク
- github.com (Go issue 3762)
- Go言語のnetパッケージのソースコード (現在のバージョン)
- DNSレコードの種類と役割 (一般的なDNSの解説)
- Go言語におけるCgo (Cgoに関する公式ブログ)