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

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

このコミットは、Go言語のnetパッケージにおけるPlan 9オペレーティングシステム向けの修正を適用し、関連するテストがすべてパスするようにしたものです。具体的には、Plan 9環境でのネットワークルックアップ(DNS TXTレコードの解決など)の改善、IPアドレスの指定方法の調整、およびTCP接続の読み書きシャットダウンに関するPlan 9固有の挙動の追加が含まれています。これにより、Goのネットワーク機能がPlan 9上でより堅牢に動作するようになります。

コミット

commit 584233608941dc579d8a4b90f463a8653f38de3a
Author: Fazlul Shahriar <fshahriar@gmail.com>
Date:   Mon Oct 31 11:47:44 2011 -0400

    net: Plan 9 fixes
    
    Makes all tests pass.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5320041

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/584233608941dc579d8a4b90f463a8653f38de3a

元コミット内容

net: Plan 9 fixes

Makes all tests pass.

R=rsc
CC=golang-dev
https://golang.org/cl/5320041

変更の背景

このコミットの主な背景は、Go言語のnetパッケージがPlan 9オペレーティングシステム上で完全に機能し、既存のテストスイートをパスするようにすることです。Goはクロスプラットフォーム対応を重視しており、様々なOS環境で一貫した動作を提供することを目指しています。Plan 9は、その独特な設計思想とファイルシステム中心のアーキテクチャを持つため、一般的なUnix系OSとは異なるネットワークインターフェースやシステムコールを持つことがあります。

具体的には、以下の問題に対処するために変更が行われました。

  1. LookupTXTの実装不足: 以前のPlan 9向けのnetパッケージでは、DNSのTXTレコードのルックアップ機能が実装されていませんでした。これは、特定のネットワークアプリケーションが正しく動作するために必要な機能であり、その欠如は互換性の問題を引き起こしていました。
  2. IPアドレスの指定の厳密化: queryCS1関数におけるIPアドレスの処理において、IP型が空の場合(len(ip) != 0)のチェックが追加されました。これにより、不正なIPアドレスが渡された場合の挙動がより堅牢になります。
  3. テストの調整: Plan 9のネットワークスタックの特性上、一部のテスト(特にTestShutdownTestTimeoutUDP/TestTimeoutTCP)がPlan 9環境では適切に動作しない、あるいは異なる挙動を示す可能性がありました。これらのテストがPlan 9上でスキップされるように調整することで、テストスイート全体がパスするようになります。
  4. TCP接続のシャットダウン: Plan 9のネットワークAPIでは、TCP接続の読み込み側または書き込み側のみをシャットダウンする機能(CloseRead, CloseWrite)が、一般的なOSとは異なる、あるいはサポートされていない場合があります。このコミットでは、これらのメソッドがPlan 9ではos.EPLAN9エラーを返すように明示的に実装され、未サポートであることを示しています。

これらの変更は、GoのnetパッケージがPlan 9環境でより安定し、予測可能な動作をするようにするための重要なステップでした。

前提知識の解説

Plan 9 from Bell Labs

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムを通じてアクセスするという徹底した「すべてはファイル」の原則に基づいています。

  • ファイルシステム中心: Plan 9では、ネットワーク接続やプロセス、グラフィカルインターフェースなどもファイルとして表現され、標準的なファイル操作(open, read, write, close)でアクセスできます。
  • 9Pプロトコル: Plan 9の分散システムは、9P(またはPlan 9 File Protocol)と呼ばれる独自のプロトコルを使用して、ネットワーク上のリソースにアクセスします。これは、ファイルシステム操作をリモートで実行するための軽量なプロトコルです。
  • ネットワークモデル: Plan 9のネットワークスタックは、Unix系OSとは異なるアプローチを取ることがあります。例えば、DNSルックアップやソケット操作のAPIが独自のものであるため、Goのようなクロスプラットフォーム言語がPlan 9をサポートするには、OS固有の実装が必要になります。

Go言語の net パッケージ

Go言語の標準ライブラリに含まれるnetパッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、IP、Unixドメインソケットなどのネットワークプロトコルをサポートし、DNSルックアップ、接続の確立、データの送受信など、様々なネットワーク操作を行うための機能を提供します。

  • クロスプラットフォーム対応: netパッケージは、内部的に各オペレーティングシステムのネットワークAPIを抽象化し、Goプログラムが異なるOS環境でも同じコードでネットワーク操作を行えるように設計されています。しかし、OS固有の特性や制限がある場合、netパッケージ内部でOSごとの実装(例: net_plan9.go, net_windows.goなど)を持つことがあります。
  • DNSルックアップ: netパッケージは、ホスト名からIPアドレスを解決するLookupHostや、特定のDNSレコード(MX, TXTなど)をルックアップする機能を提供します。これらの機能は、OSのDNSリゾルバや、場合によっては独自のDNSクライアント実装を利用します。

DNS TXTレコード

DNS(Domain Name System)のTXTレコードは、ドメイン名に関連付けられた任意のテキスト情報を格納するために使用されるリソースレコードの一種です。

  • 用途: 主に、ドメインの所有権確認、SPF(Sender Policy Framework)やDKIM(DomainKeys Identified Mail)などのメール認証、DMARC(Domain-based Message Authentication, Reporting & Conformance)ポリシーの公開など、様々な目的で利用されます。
  • 構造: TXTレコードは、キーと値のペア、または単一のテキスト文字列として表現されることが多く、特定のサービスやプロトコルがその情報を解釈します。

技術的詳細

このコミットは、Goのnetパッケージ内のPlan 9固有のファイル(lookup_plan9.go, tcpsock_plan9.go)と、テストファイル(lookup_test.go, net_test.go, timeout_test.go)にわたる変更を含んでいます。

src/pkg/net/lookup_plan9.go の変更

  • queryCS1関数のip.IsUnspecified()チェックの修正:
    • 変更前: if !ip.IsUnspecified() { ... }
    • 変更後: if len(ip) != 0 && !ip.IsUnspecified() { ... }
    • この変更は、ipが空のスライスである場合(len(ip) == 0)にIsUnspecified()が呼び出される前にチェックを追加することで、潜在的なパニックを防ぎ、より堅牢なIPアドレスの処理を保証します。IsUnspecified()は、IPアドレスが指定されていない(例: 0.0.0.0::)場合にtrueを返しますが、ipスライス自体が空であるケースは別途考慮する必要があります。
  • LookupTXT関数の実装:
    • 変更前: return nil, os.NewError("net.LookupTXT is not implemented on Plan 9")
    • 変更後: queryDNS関数を使用してDNS TXTレコードを実際にルックアップするロジックが追加されました。
    • queryDNS(name, "txt")を呼び出してTXTレコードを取得し、結果の各行を解析してタブ文字(\t)で区切り、実際のTXTデータ部分(タブ以降の文字列)を抽出してtxtスライスに追加します。これにより、Plan 9上でのDNS TXTレコードの解決が可能になりました。

src/pkg/net/lookup_test.go の変更

  • TestGmailTXTテストの条件変更:
    • 変更前: if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { t.Logf("LookupTXT is not implemented on Windows or Plan 9") return }
    • 変更後: if runtime.GOOS == "windows" { t.Logf("LookupTXT is not implemented on Windows") return }
    • LookupTXTがPlan 9で実装されたため、Plan 9環境でのテストスキップ条件が削除されました。これにより、Plan 9上でもTestGmailTXTが実行されるようになります。

src/pkg/net/net_test.go の変更

  • TestShutdownテストの条件追加:
    • import "runtime"が追加されました。
    • if runtime.GOOS == "plan9" { return }という条件がTestShutdown関数の冒頭に追加されました。
    • これは、Plan 9のネットワークスタックがTCP接続のシャットダウン(CloseRead, CloseWrite)に関して異なる挙動を示す、またはテストが意図する動作を完全にサポートしていない可能性があるため、Plan 9上ではこのテストをスキップするようにしています。

src/pkg/net/tcpsock_plan9.go の変更

  • TCPConnCloseReadCloseWriteメソッドを追加:
    • CloseRead()CloseWrite()メソッドがTCPConn型に追加されました。
    • これらのメソッドは、c.ok()(接続が有効かどうかのチェック)が成功した場合でも、常にos.EPLAN9エラーを返します。
    • これは、Plan 9のネットワークAPIがTCP接続の読み込み側または書き込み側のみを個別にシャットダウンする機能を直接サポートしていない、またはその実装が複雑であるため、Goのnetパッケージではこの機能をPlan 9上で明示的に未サポートとしてマークしていることを示しています。これにより、開発者はPlan 9環境でこれらのメソッドを呼び出した際に、予期せぬ動作ではなく、明確なエラーを受け取ることができます。

src/pkg/net/timeout_test.go の変更

  • TestTimeoutUDPTestTimeoutTCPテストの条件追加:
    • import "runtime"が追加されました。
    • if runtime.GOOS == "plan9" { return }という条件がTestTimeoutUDPTestTimeoutTCP関数の冒頭に追加されました。
    • これらのテストは、ネットワークタイムアウトの挙動を検証するものですが、Plan 9のネットワークスタックにおけるタイムアウト処理が他のOSと異なる、またはテストの意図するシナリオを再現できない可能性があるため、Plan 9上ではスキップされます。

これらの変更は、Plan 9という特定のOS環境におけるGoのネットワーク機能の互換性と堅牢性を向上させるための、きめ細やかな調整を示しています。

コアとなるコードの変更箇所

src/pkg/net/lookup_plan9.go

@@ -49,7 +49,7 @@ func queryCS(net, host, service string) (res []string, err os.Error) {

 func queryCS1(net string, ip IP, port int) (clone, dest string, err os.Error) {
 	ips := "*"
-	if !ip.IsUnspecified() {
+	if len(ip) != 0 && !ip.IsUnspecified() {
 		ips = ip.String()
 	}
 	lines, err := queryCS(net, ips, itoa(port))
@@ -215,7 +215,16 @@ func LookupMX(name string) (mx []*MX, err os.Error) {

 // LookupTXT returns the DNS TXT records for the given domain name.
 func LookupTXT(name string) (txt []string, err os.Error) {
-	return nil, os.NewError("net.LookupTXT is not implemented on Plan 9")
+	lines, err := queryDNS(name, "txt")
+	if err != nil {
+		return
+	}
+	for _, line := range lines {
+		if i := byteIndex(line, '\t'); i >= 0 {
+			txt = append(txt, line[i+1:])
+		}
+	}
+	return
 }

src/pkg/net/tcpsock_plan9.go

@@ -16,6 +16,24 @@ type TCPConn struct {
 	plan9Conn
 }

+// CloseRead shuts down the reading side of the TCP connection.
+// Most callers should just use Close.
+func (c *TCPConn) CloseRead() os.Error {
+	if !c.ok() {
+		return os.EINVAL
+	}
+	return os.EPLAN9
+}
+
+// CloseWrite shuts down the writing side of the TCP connection.
+// Most callers should just use Close.
+func (c *TCPConn) CloseWrite() os.Error {
+	if !c.ok() {
+		return os.EINVAL
+	}
+	return os.EPLAN9
+}
+
 // DialTCP connects to the remote address raddr on the network net,
 // which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
 // as the local address for the connection.

コアとなるコードの解説

src/pkg/net/lookup_plan9.go の変更点

  1. queryCS1関数のIPアドレスチェックの強化:
    • if len(ip) != 0 && !ip.IsUnspecified(): この行は、ipスライスが空でないこと(len(ip) != 0)を最初に確認し、その後にIPアドレスが未指定でないこと(!ip.IsUnspecified())をチェックします。これにより、ipが空のスライスである場合にip.IsUnspecified()を呼び出すことによる潜在的なランタイムエラーを防ぎます。これは、GoのIPアドレス表現がバイトスライスであるため、空のスライスが有効なIPアドレスではないことを考慮した堅牢化です。
  2. LookupTXT関数の実装:
    • lines, err := queryDNS(name, "txt"): この行は、queryDNSという内部ヘルパー関数を呼び出し、指定されたドメイン名(name)とレコードタイプ("txt")に基づいてDNSクエリを実行します。これにより、Plan 9環境でDNS TXTレコードを取得するための基盤が提供されます。
    • for _, line := range lines { ... }: queryDNSから返された各行をループ処理します。
    • if i := byteIndex(line, '\t'); i >= 0 { ... }: 各行内でタブ文字(\t)のインデックスを検索します。Plan 9のDNS応答形式では、TXTレコードのデータがタブで区切られていることを想定しています。
    • txt = append(txt, line[i+1:]): タブ文字が見つかった場合、タブの直後から行の終わりまでの部分を抽出して、txtスライスに追加します。これは、実際のTXTレコードのテキストデータがタブの後に続くという仮定に基づいています。この実装により、Plan 9上でGoプログラムがDNS TXTレコードを正常に解決できるようになります。

src/pkg/net/tcpsock_plan9.go の変更点

  1. CloseRead()メソッドの追加:
    • func (c *TCPConn) CloseRead() os.Error: TCPConn型にCloseReadメソッドが追加されました。このメソッドは、TCP接続の読み込み側のみをシャットダウンすることを意図しています。
    • if !c.ok() { return os.EINVAL }: 接続が有効な状態でない場合(例: 既に閉じられている場合)は、os.EINVAL(無効な引数)エラーを返します。
    • return os.EPLAN9: 接続が有効であっても、このメソッドは常にos.EPLAN9エラーを返します。これは、Plan 9のネットワークAPIがTCP接続の読み込み側のみを個別にシャットダウンする機能を直接サポートしていない、またはGoのnetパッケージがPlan 9上でその機能を提供しないことを明示的に示しています。これにより、Plan 9環境でこの機能を使用しようとした際に、明確なエラーハンドリングが可能になります。
  2. CloseWrite()メソッドの追加:
    • func (c *TCPConn) CloseWrite() os.Error: TCPConn型にCloseWriteメソッドが追加されました。このメソッドは、TCP接続の書き込み側のみをシャットダウンすることを意図しています。
    • if !c.ok() { return os.EINVAL }: CloseReadと同様に、接続が有効な状態でない場合はos.EINVALエラーを返します。
    • return os.EPLAN9: CloseReadと同様に、Plan 9ではこの機能がサポートされていないことを示すために、常にos.EPLAN9エラーを返します。

これらの変更は、GoのnetパッケージがPlan 9という特定のOSの特性に合わせて、ネットワーク操作の挙動を調整し、互換性と堅牢性を確保するための具体的な実装例を示しています。特に、LookupTXTの実装は機能追加であり、CloseRead/CloseWriteの実装はPlan 9の制約を考慮したエラーハンドリングの追加です。

関連リンク

  • Go CL 5320041: https://golang.org/cl/5320041

参考にした情報源リンク