[インデックス 13294] ファイルの概要
このコミットは、Go言語のネットワークパッケージ(net
)におけるFreeBSD固有の問題を修正するものです。具体的には、cgoAddrInfoFlags()
関数が返すアドレス情報フラグの計算方法を調整し、FreeBSDのlibc
(C標準ライブラリ)がgetaddrinfo
システムコールに対してより厳密なフラグチェックを行うことに対応しています。
コミット
commit 58993e514ec8ee306b305df7db761f73ae522d3a
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Jun 6 22:03:31 2012 +0800
net: fix cgoAddrInfoFlags() on FreeBSD
CL 6250075 removed AI_MASK mask on all BSD variants,
however FreeBSD's AI_MASK does not include AI_V4MAPPED
and AI_ALL, and its libc is strict about the ai_flags.
This will fix the FreeBSD builder.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6305054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/58993e514ec8ee306b305df7db761f73ae522d3a
元コミット内容
net: fix cgoAddrInfoFlags() on FreeBSD
CL 6250075 (おそらく以前の変更リスト) は、すべてのBSD派生OSにおいて AI_MASK
マスクを削除しました。しかし、FreeBSDの AI_MASK
は AI_V4MAPPED
および AI_ALL
を含んでおらず、その libc
は ai_flags
に対して厳密です。
この変更により、FreeBSDのビルドが修正されます。
変更の背景
このコミットの背景には、Go言語のネットワークスタックが、ホスト名からIPアドレスへの変換を行う際に使用する getaddrinfo
システムコールと、その引数であるアドレス情報フラグ(ai_flags
)の扱いに関する問題があります。
以前の変更(CL 6250075)では、BSD系のOS全体で AI_MASK
を使用してフラグをマスクする処理が削除されました。これは、おそらく他のBSD派生OS(例えばOpenBSDやNetBSDなど)では、AI_V4MAPPED
や AI_ALL
といったフラグが AI_MASK
に含まれており、マスク処理が不要、あるいは特定の挙動を期待していたためと考えられます。
しかし、FreeBSDの libc
の実装は、これらのフラグ(AI_V4MAPPED
と AI_ALL
)を AI_MASK
に含んでいませんでした。AI_MASK
は、getaddrinfo
に渡されるフラグのうち、システムが認識し、処理できる有効なフラグのビットマスクを定義します。FreeBSDの libc
は、AI_MASK
で許可されていないフラグが ai_flags
に含まれている場合、エラーを返すなど、非常に厳密なチェックを行います。
この厳密なチェックのため、Goの net
パッケージが cgoAddrInfoFlags()
で生成するフラグセットがFreeBSDの libc
にとって無効となり、結果としてFreeBSD上でのビルドやネットワーク関連の機能が正しく動作しない、あるいはクラッシュする問題が発生していました。このコミットは、このFreeBSD固有の互換性問題を解決し、FreeBSD環境でのGoの安定性を確保することを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
-
getaddrinfo
システムコール:getaddrinfo
は、POSIX標準で定義されているネットワークプログラミングAPIの一つで、ホスト名(例:www.example.com
)やサービス名(例:http
)を、ネットワークアドレス構造体(sockaddr
)のリストに変換するために使用されます。- これは、IPv4とIPv6の両方に対応しており、アプリケーションがどちらのプロトコルを使用するかを意識せずに、ホスト名解決を行えるように設計されています。
- 引数として、ホスト名、サービス名、そして
addrinfo
構造体へのポインタを受け取ります。addrinfo
構造体には、解決の挙動を制御するための様々なフラグ(ai_flags
)が含まれます。
-
ai_flags
:addrinfo
構造体の一部であり、getaddrinfo
の動作をカスタマイズするためのビットフラグの集合です。- 主要なフラグには以下のようなものがあります。
AI_CANONNAME
: 解決されたホストの正規名(canonical name)をai_canonname
フィールドに設定するよう要求します。AI_V4MAPPED
: IPv6アドレスを要求し、かつIPv6アドレスが見つからない場合に、IPv4アドレスをIPv6形式にマップして返すことを許可します。これにより、IPv6のみをサポートするアプリケーションがIPv4ホストにも接続できるようになります。AI_ALL
:AI_V4MAPPED
と組み合わせて使用され、IPv6アドレスと、IPv4マップされたIPv6アドレスの両方を返します。これにより、可能な限り多くのIPアドレスが返されることになります。AI_MASK
: これは実際のai_flags
の値として渡されるフラグではなく、getaddrinfo
が認識する有効なフラグのビットマスクです。システムやlibc
のバージョンによって、このマスクに含まれるフラグの種類が異なります。getaddrinfo
の実装は、このAI_MASK
に含まれないフラグが渡された場合にエラーを返すことがあります。
-
Cgo:
- Go言語の機能の一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりすることを可能にします。
- このコミットでは、Goの
net
パッケージがC言語のgetaddrinfo
システムコールを呼び出すためにCgoを使用しています。
-
libc
(C標準ライブラリ):- C言語で書かれたプログラムがオペレーティングシステムの機能(ファイルI/O、メモリ管理、ネットワーク通信など)を利用するための標準的な関数を提供するライブラリです。
- UNIX系OSでは、
glibc
(GNU C Library) やmusl libc
、BSD系OSでは独自のlibc
実装(例えばFreeBSDのlibc
)が存在します。これらの実装は、標準に準拠しつつも、細部で異なる挙動を示すことがあります。特に、getaddrinfo
のような複雑なAPIでは、フラグの解釈やエラー処理の厳密さに違いが出ることがあります。
技術的詳細
このコミットの技術的な核心は、FreeBSDの libc
が getaddrinfo
システムコールに渡される ai_flags
の値に対して、他のBSD派生OSやLinuxの glibc
とは異なる、より厳密な検証を行う点にあります。
Goの net
パッケージは、クロスプラットフォームで動作するために、各OSのC標準ライブラリの getaddrinfo
をCgo経由で呼び出しています。cgoAddrInfoFlags()
関数は、この getaddrinfo
に渡す ai_flags
の値を生成する役割を担っています。
以前のコードでは、C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
というフラグの組み合わせを直接返していました。これは、ホストの正規名を要求し、IPv4マップされたIPv6アドレスと、すべての可能なIPアドレスを返すことを意図しています。
しかし、FreeBSDの libc
の getaddrinfo
実装では、AI_V4MAPPED
や AI_ALL
といったフラグが、そのシステムが定義する有効なフラグの集合(AI_MASK
で示される)に含まれていませんでした。つまり、FreeBSDの AI_MASK
は、AI_V4MAPPED
や AI_ALL
のビットが立っていない状態でした。
getaddrinfo
の仕様では、実装は AI_MASK
に含まれないフラグが渡された場合にエラーを返すことが許容されています。FreeBSDの libc
はこの仕様に厳密に従い、AI_MASK
に含まれない AI_V4MAPPED
や AI_ALL
が設定された ai_flags
を受け取ると、無効な引数として処理し、エラーを返していました。これにより、Goのネットワーク関連のテストやアプリケーションがFreeBSD上で失敗する原因となっていました。
このコミットの修正は、生成されるフラグセットを AI_MASK
でビットAND演算することによって、この問題を解決します。(C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
とすることで、Goが意図するフラグのうち、FreeBSDの libc
が有効と認識するフラグのみが最終的な ai_flags
の値として getaddrinfo
に渡されるようになります。これにより、FreeBSDの厳密なチェックをパスし、ネットワーク解決が正常に行われるようになります。
この修正は、Goが特定のOSの libc
の挙動の差異に対応する必要があることを示しており、クロスプラットフォーム開発における移植性の課題の一例と言えます。
コアとなるコードの変更箇所
変更は src/pkg/net/cgo_bsd.go
ファイルの cgoAddrInfoFlags()
関数にあります。
--- a/src/pkg/net/cgo_bsd.go
+++ b/src/pkg/net/cgo_bsd.go
@@ -12,5 +12,5 @@ package net
import "C"
func cgoAddrInfoFlags() C.int {
- return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
+ return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
}
コアとなるコードの解説
cgoAddrInfoFlags()
関数は、Goの net
パッケージがC言語の getaddrinfo
関数を呼び出す際に使用する ai_flags
引数の値を生成します。
-
変更前:
return C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL
この行は、
AI_CANONNAME
、AI_V4MAPPED
、AI_ALL
の各フラグをビットOR演算で結合した値を直接返していました。これは、ホストの正規名を要求し、IPv4マップされたIPv6アドレスと、すべての可能なIPアドレスを解決結果に含めることを意図していました。しかし、FreeBSDのlibc
はAI_V4MAPPED
とAI_ALL
を有効なフラグとして認識しないため、この値がgetaddrinfo
に渡されるとエラーが発生していました。 -
変更後:
return (C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL) & C.AI_MASK
この変更では、元のフラグの組み合わせ
(C.AI_CANONNAME | C.AI_V4MAPPED | C.AI_ALL)
に対して、さらにC.AI_MASK
とのビットAND演算が追加されています。C.AI_MASK
は、現在のシステム(この場合はFreeBSD)のlibc
がgetaddrinfo
で有効と認識するフラグのビットマスクです。- ビットAND演算
&
を使用することで、Goが設定したいフラグのうち、C.AI_MASK
で許可されているビットのみが最終的なai_flags
の値として残ります。 - これにより、FreeBSDの
libc
が無効なフラグとして認識するビットがクリアされ、getaddrinfo
が正常に動作するようになります。結果として、FreeBSD環境でのGoのネットワーク関連機能が安定して動作するようになります。
この修正は、特定のOSの libc
の実装の詳細に合わせた、堅牢なクロスプラットフォーム対応の一例です。
関連リンク
getaddrinfo
manページ (Linux): https://man7.org/linux/man-pages/man3/getaddrinfo.3.htmlgetaddrinfo
manページ (FreeBSD): https://www.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3- Go言語のCgoに関するドキュメント: https://go.dev/blog/cgo
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/ (CL 6305054 を検索すると、このコミットの元のレビューページが見つかる可能性がありますが、古いCLはアーカイブされている場合があります。)
- RFC 3493: Basic Socket Interface Extensions for IPv6 (getaddrinfoの仕様を含む): https://datatracker.ietf.org/doc/html/rfc3493