[インデックス 17134] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるPlan 9環境でのSRVレコードのルックアップ順序に関するバグ修正です。具体的には、lookup_plan9.go
ファイル内のlookupSRV
関数が、SRVレコードの結果を処理する際に、weight
、priority
、port
の順序を誤って解釈していた問題を修正しています。
コミット
commit 3319db4c94fe1083db96e57b4ee8de780a8c88bf
Author: Nicolas Owens <mischief@offblast.org>
Date: Fri Aug 9 14:16:43 2013 -0700
net: fix LookupSRV ordering on plan 9
lookup_plan9.go's lookupSRV is using the wrong order for srv results. order should be weight, priority, port, following the response from /net/dns:
chi Aug 9 20:31:13 Rread tag 20 count 61 '_xmpp-client._tcp.offblast.org srv 5 0 5222 iota.offblast.org' 72
R=golang-dev, bradfitz
CC=ality, golang-dev, r, rsc
https://golang.org/cl/12708043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3319db4c94fe1083db96e57b4ee8de780a8c88bf
元コミット内容
net: fix LookupSRV ordering on plan 9
lookup_plan9.go
のlookupSRV
がSRV結果の順序を誤って使用していました。/net/dns
からの応答に従い、順序はweight
、priority
、port
であるべきです。
例:
chi Aug 9 20:31:13 Rread tag 20 count 61 '_xmpp-client._tcp.offblast.org srv 5 0 5222 iota.offblast.org' 72
変更の背景
この変更は、Go言語のnet
パッケージがPlan 9オペレーティングシステム上でSRV (Service Record) レコードをルックアップする際の、データの解釈ミスを修正するために行われました。SRVレコードは、特定のサービスが利用可能なホスト名とポート番号をDNSを通じて提供するために使用されます。RFC 2782で定義されており、サービスを提供するサーバーの優先度(priority)、重み(weight)、ポート番号(port)、ターゲットホスト(target)などの情報を含みます。
Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、そのネットワークスタックは独特なファイルシステムベースのインターフェースを持っています。Go言語のnet
パッケージは、異なるOS環境に対応するために、それぞれのOS固有のネットワークAPIをラップしています。この場合、Plan 9の/net/dns
インターフェースからSRVレコードの情報を読み取る際に、フィールドの順序を誤って解釈していたことが問題でした。
具体的には、Plan 9の/net/dns
が返すSRVレコードの文字列表現において、weight
、priority
、port
の各フィールドが期待される順序と異なっていたため、GoのlookupSRV
関数がこれらの値を間違った変数に割り当てていました。これにより、SRVレコードに基づくサービス選択ロジック(例えば、負荷分散やフェイルオーバー)が正しく機能しない可能性がありました。このコミットは、このフィールドのパース順序の誤りを修正し、Plan 9環境でのSRVルックアップの正確性を確保することを目的としています。
前提知識の解説
SRVレコード (Service Record)
SRVレコードは、DNS (Domain Name System) の一種で、特定のサービスが利用可能なホスト名とポート番号を公開するために使用されます。RFC 2782で定義されており、主に以下のような目的で利用されます。
- サービスディスカバリ: クライアントが特定のサービス(例: SIP、XMPP、LDAPなど)を提供しているサーバーを、そのIPアドレスを知らなくても見つけられるようにします。
- 負荷分散: 複数のサーバーが同じサービスを提供している場合、SRVレコードの
priority
とweight
フィールドを使用して、クライアントがどのサーバーに接続すべきかを決定するヒントを提供します。 - フェイルオーバー: サーバーに障害が発生した場合に、別のサーバーに切り替えるための情報を提供します。
SRVレコードの一般的な形式は以下の通りです。
_service._proto.name. TTL class SRV priority weight port target.
_service
: サービス名(例:_xmpp-client
)_proto
: プロトコル(例:_tcp
、_udp
)name
: ドメイン名priority
: 優先度。小さい値ほど優先度が高い。同じ優先度のレコードがある場合、weight
が考慮される。weight
: 重み。同じpriority
を持つレコード間で、接続の割合を決定するために使用される。値が大きいほど、そのサーバーに接続される可能性が高くなる。port
: サービスがリッスンしているポート番号。target
: サービスを提供しているホストのFQDN (Fully Qualified Domain Name)。
Plan 9 オペレーティングシステム
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムを通じてアクセスするという哲学に基づいています。
- ファイルシステム中心主義: Plan 9では、ネットワーク接続、プロセス、グラフィカルインターフェースなど、あらゆるものがファイルとして表現され、標準のファイルシステムインターフェース(
open
,read
,write
,close
など)を通じて操作されます。 /net
ファイルシステム: ネットワーク関連の操作は、/net
という特殊なファイルシステムを通じて行われます。例えば、DNSルックアップは/net/dns
ファイルを読み書きすることで行われます。- 分散システム: Plan 9は、複数のマシンが協調して動作する分散システムとして設計されており、リソースの共有と透過的なアクセスを重視しています。
このコミットで問題となっているのは、Goのnet
パッケージがPlan 9の/net/dns
からSRVレコードの情報を読み取る際の、特定の文字列フォーマットの解釈です。
Go言語のnet
パッケージ
Go言語の標準ライブラリであるnet
パッケージは、ネットワークプログラミングのための基本的な機能を提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルをサポートし、DNSルックアップ、HTTPクライアント/サーバー、TLS/SSLなどの高レベルな機能も提供します。
net
パッケージは、異なるオペレーティングシステム(Linux, macOS, Windows, Plan 9など)に対応するために、OS固有のシステムコールやAPIを抽象化しています。lookup_plan9.go
のようなファイルは、特定のOS(この場合はPlan 9)に特化した実装を提供し、GoのネットワークAPIが異なる環境で一貫して動作するようにしています。
技術的詳細
このコミットの技術的詳細は、Plan 9の/net/dns
インターフェースから返されるSRVレコードの文字列フォーマットと、Go言語のnet
パッケージがその文字列をパースする際のインデックスのずれにあります。
コミットメッセージの例にあるように、Plan 9の/net/dns
はSRVレコードを以下のような形式で返します。
_xmpp-client._tcp.offblast.org srv 5 0 5222 iota.offblast.org
この文字列をスペースで分割すると、各フィールドは以下のインデックスに対応します(0-based)。
f[0]
:_xmpp-client._tcp.offblast.org
(サービス名とプロトコル、ドメイン名)f[1]
:srv
(レコードタイプ)f[2]
:5
(この例ではweight
に相当する値)f[3]
:0
(この例ではpriority
に相当する値)f[4]
:5222
(この例ではport
に相当する値)f[5]
:iota.offblast.org
(ターゲットホスト)
元のコードでは、lookup_plan9.go
のlookupSRV
関数内で、これらのフィールドを以下のようにパースしていました。
port, _, portOk := dtoi(f[2], 0) // 誤ってweightの値をportとして取得
priority, _, priorityOk := dtoi(f[3], 0) // 正しくpriorityの値を取得
weight, _, weightOk := dtoi(f[4], 0) // 誤ってportの値をweightとして取得
この誤りにより、f[2]
(本来weight
)がport
として、f[4]
(本来port
)がweight
として解釈されていました。priority
はf[3]
から正しく取得されていました。
このコミットでは、このインデックスのずれを修正し、正しいフィールドを正しい変数に割り当てるように変更しています。
port, _, portOk := dtoi(f[4], 0) // f[4]からportの値を正しく取得
priority, _, priorityOk := dtoi(f[3], 0) // f[3]からpriorityの値を正しく取得 (変更なし)
weight, _, weightOk := dtoi(f[2], 0) // f[2]からweightの値を正しく取得
この修正により、Plan 9環境でSRVレコードをルックアップした際に、weight
、priority
、port
の各値がRFC 2782の定義とPlan 9の/net/dns
の出力フォーマットに従って正しく解釈されるようになります。これにより、SRVレコードを利用したサービスディスカバリや負荷分散のロジックが、Plan 9環境でも意図通りに機能するようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/net/lookup_plan9.go
ファイルにあります。
--- a/src/pkg/net/lookup_plan9.go
+++ b/src/pkg/net/lookup_plan9.go
@@ -186,9 +186,9 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err
if len(f) < 6 {
continue
}
- port, _, portOk := dtoi(f[2], 0)
+ port, _, portOk := dtoi(f[4], 0)
priority, _, priorityOk := dtoi(f[3], 0)
- weight, _, weightOk := dtoi(f[4], 0)
+ weight, _, weightOk := dtoi(f[2], 0)
if !(portOk && priorityOk && weightOk) {
continue
}
コアとなるコードの解説
変更されたのは、lookupSRV
関数内の以下の3行です。
-
port
の取得:- 変更前:
port, _, portOk := dtoi(f[2], 0)
- 変更後:
port, _, portOk := dtoi(f[4], 0)
f[2]
からf[4]
に変更されました。これは、Plan 9の/net/dns
が返すSRVレコードの文字列において、ポート番号が配列の4番目の要素(インデックス4)に位置しているためです。
- 変更前:
-
priority
の取得:- 変更前:
priority, _, priorityOk := dtoi(f[3], 0)
- 変更後:
priority, _, priorityOk := dtoi(f[3], 0)
この行は変更されていません。priority
は元々正しいインデックスf[3]
から取得されていました。
- 変更前:
-
weight
の取得:- 変更前:
weight, _, weightOk := dtoi(f[4], 0)
- 変更後:
weight, _, weightOk := dtoi(f[2], 0)
f[4]
からf[2]
に変更されました。これは、Plan 9の/net/dns
が返すSRVレコードの文字列において、重み(weight)が配列の2番目の要素(インデックス2)に位置しているためです。
- 変更前:
dtoi
関数は、文字列を整数に変換するヘルパー関数です。この修正により、f
配列から取得される各数値フィールド(port
, priority
, weight
)が、Plan 9の/net/dns
の出力フォーマットとRFC 2782の定義に従って、正しい意味を持つ変数に割り当てられるようになりました。これにより、Goのnet
パッケージがPlan 9環境でSRVレコードを正しくパースし、利用できるようになります。
関連リンク
- Go言語の
net
パッケージのドキュメント: https://pkg.go.dev/net - RFC 2782 - A DNS RR for specifying the location of services (SRV): https://datatracker.ietf.org/doc/html/rfc2782
- Plan 9 from Bell Labs: https://9p.io/plan9/
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されている
https://golang.org/cl/12708043
はGerritの変更リストへのリンクです) - RFC 2782
- Plan 9のドキュメントと関連情報
- Go言語の
net
パッケージのソースコード - 一般的なDNSおよびネットワークプロトコルに関する知識