Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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_MASKAI_V4MAPPED および AI_ALL を含んでおらず、その libcai_flags に対して厳密です。

この変更により、FreeBSDのビルドが修正されます。

変更の背景

このコミットの背景には、Go言語のネットワークスタックが、ホスト名からIPアドレスへの変換を行う際に使用する getaddrinfo システムコールと、その引数であるアドレス情報フラグ(ai_flags)の扱いに関する問題があります。

以前の変更(CL 6250075)では、BSD系のOS全体で AI_MASK を使用してフラグをマスクする処理が削除されました。これは、おそらく他のBSD派生OS(例えばOpenBSDやNetBSDなど)では、AI_V4MAPPEDAI_ALL といったフラグが AI_MASK に含まれており、マスク処理が不要、あるいは特定の挙動を期待していたためと考えられます。

しかし、FreeBSDの libc の実装は、これらのフラグ(AI_V4MAPPEDAI_ALL)を AI_MASK に含んでいませんでした。AI_MASK は、getaddrinfo に渡されるフラグのうち、システムが認識し、処理できる有効なフラグのビットマスクを定義します。FreeBSDの libc は、AI_MASK で許可されていないフラグが ai_flags に含まれている場合、エラーを返すなど、非常に厳密なチェックを行います。

この厳密なチェックのため、Goの net パッケージが cgoAddrInfoFlags() で生成するフラグセットがFreeBSDの libc にとって無効となり、結果としてFreeBSD上でのビルドやネットワーク関連の機能が正しく動作しない、あるいはクラッシュする問題が発生していました。このコミットは、このFreeBSD固有の互換性問題を解決し、FreeBSD環境でのGoの安定性を確保することを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  1. getaddrinfo システムコール:

    • getaddrinfo は、POSIX標準で定義されているネットワークプログラミングAPIの一つで、ホスト名(例: www.example.com)やサービス名(例: http)を、ネットワークアドレス構造体(sockaddr)のリストに変換するために使用されます。
    • これは、IPv4とIPv6の両方に対応しており、アプリケーションがどちらのプロトコルを使用するかを意識せずに、ホスト名解決を行えるように設計されています。
    • 引数として、ホスト名、サービス名、そして addrinfo 構造体へのポインタを受け取ります。addrinfo 構造体には、解決の挙動を制御するための様々なフラグ(ai_flags)が含まれます。
  2. 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 に含まれないフラグが渡された場合にエラーを返すことがあります。
  3. Cgo:

    • Go言語の機能の一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりすることを可能にします。
    • このコミットでは、Goの net パッケージがC言語の getaddrinfo システムコールを呼び出すためにCgoを使用しています。
  4. libc (C標準ライブラリ):

    • C言語で書かれたプログラムがオペレーティングシステムの機能(ファイルI/O、メモリ管理、ネットワーク通信など)を利用するための標準的な関数を提供するライブラリです。
    • UNIX系OSでは、glibc (GNU C Library) や musl libc、BSD系OSでは独自の libc 実装(例えばFreeBSDの libc)が存在します。これらの実装は、標準に準拠しつつも、細部で異なる挙動を示すことがあります。特に、getaddrinfo のような複雑なAPIでは、フラグの解釈やエラー処理の厳密さに違いが出ることがあります。

技術的詳細

このコミットの技術的な核心は、FreeBSDの libcgetaddrinfo システムコールに渡される 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の libcgetaddrinfo 実装では、AI_V4MAPPEDAI_ALL といったフラグが、そのシステムが定義する有効なフラグの集合(AI_MASK で示される)に含まれていませんでした。つまり、FreeBSDの AI_MASK は、AI_V4MAPPEDAI_ALL のビットが立っていない状態でした。

getaddrinfo の仕様では、実装は AI_MASK に含まれないフラグが渡された場合にエラーを返すことが許容されています。FreeBSDの libc はこの仕様に厳密に従い、AI_MASK に含まれない AI_V4MAPPEDAI_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_CANONNAMEAI_V4MAPPEDAI_ALL の各フラグをビットOR演算で結合した値を直接返していました。これは、ホストの正規名を要求し、IPv4マップされたIPv6アドレスと、すべての可能なIPアドレスを解決結果に含めることを意図していました。しかし、FreeBSDの libcAI_V4MAPPEDAI_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)の libcgetaddrinfo で有効と認識するフラグのビットマスクです。
    • ビットAND演算 & を使用することで、Goが設定したいフラグのうち、C.AI_MASK で許可されているビットのみが最終的な ai_flags の値として残ります。
    • これにより、FreeBSDの libc が無効なフラグとして認識するビットがクリアされ、getaddrinfo が正常に動作するようになります。結果として、FreeBSD環境でのGoのネットワーク関連機能が安定して動作するようになります。

この修正は、特定のOSの libc の実装の詳細に合わせた、堅牢なクロスプラットフォーム対応の一例です。

関連リンク

参考にした情報源リンク