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

[インデックス 17134] ファイルの概要

このコミットは、Go言語のnetパッケージにおけるPlan 9環境でのSRVレコードのルックアップ順序に関するバグ修正です。具体的には、lookup_plan9.goファイル内のlookupSRV関数が、SRVレコードの結果を処理する際に、weightpriorityportの順序を誤って解釈していた問題を修正しています。

コミット

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.golookupSRVがSRV結果の順序を誤って使用していました。/net/dnsからの応答に従い、順序はweightpriorityportであるべきです。

例: 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レコードの文字列表現において、weightpriorityportの各フィールドが期待される順序と異なっていたため、GoのlookupSRV関数がこれらの値を間違った変数に割り当てていました。これにより、SRVレコードに基づくサービス選択ロジック(例えば、負荷分散やフェイルオーバー)が正しく機能しない可能性がありました。このコミットは、このフィールドのパース順序の誤りを修正し、Plan 9環境でのSRVルックアップの正確性を確保することを目的としています。

前提知識の解説

SRVレコード (Service Record)

SRVレコードは、DNS (Domain Name System) の一種で、特定のサービスが利用可能なホスト名とポート番号を公開するために使用されます。RFC 2782で定義されており、主に以下のような目的で利用されます。

  • サービスディスカバリ: クライアントが特定のサービス(例: SIP、XMPP、LDAPなど)を提供しているサーバーを、そのIPアドレスを知らなくても見つけられるようにします。
  • 負荷分散: 複数のサーバーが同じサービスを提供している場合、SRVレコードのpriorityweightフィールドを使用して、クライアントがどのサーバーに接続すべきかを決定するヒントを提供します。
  • フェイルオーバー: サーバーに障害が発生した場合に、別のサーバーに切り替えるための情報を提供します。

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.golookupSRV関数内で、これらのフィールドを以下のようにパースしていました。

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として解釈されていました。priorityf[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レコードをルックアップした際に、weightpriorityportの各値が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行です。

  1. portの取得:

    • 変更前: port, _, portOk := dtoi(f[2], 0)
    • 変更後: port, _, portOk := dtoi(f[4], 0) f[2]からf[4]に変更されました。これは、Plan 9の/net/dnsが返すSRVレコードの文字列において、ポート番号が配列の4番目の要素(インデックス4)に位置しているためです。
  2. priorityの取得:

    • 変更前: priority, _, priorityOk := dtoi(f[3], 0)
    • 変更後: priority, _, priorityOk := dtoi(f[3], 0) この行は変更されていません。priorityは元々正しいインデックスf[3]から取得されていました。
  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言語のコミット履歴 (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およびネットワークプロトコルに関する知識