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

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

このコミットは、Go言語のnetパッケージにおける、Unixネットワーク上の着信接続のネットワーク名処理に関するバグ修正です。具体的には、src/pkg/net/conn_test.gosrc/pkg/net/unixsock_posix.goの2つのファイルが変更されています。

コミット

  • コミットハッシュ: 731e6f7d1d7238c8465f44691836a8a865d4cdb9
  • 作者: Mikio Hara mikioh.mikioh@gmail.com
  • コミット日時: 2014年1月29日(水) 09:51:31 +0900

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

https://github.com/golang/go/commit/731e6f7d1d7238c8465f44691836a8a865d4cdb9

元コミット内容

net: fix incoming connection's network name handling on unix networks

Fixes #7183.

LGTM=iant
R=golang-codereviews, gobot, iant
CC=golang-codereviews
https://golang.org/cl/57520043

変更の背景

このコミットは、GoのnetパッケージがUnixドメインソケットを介した着信接続のネットワーク名を正しく処理しないというバグを修正するために行われました。コミットメッセージにはFixes #7183とありますが、GoのGitHubリポジトリでこの番号のIssueは見つかりませんでした。しかし、Fixes #XXXXという表記は、通常、特定のバグトラッカーのIssueを修正したことを示す慣例的な表現です。この場合、Goの内部的なバグトラッカーや、非常に古い、あるいはクローズされたIssueを参照している可能性があります。

問題の核心は、Unixドメインソケットにおいて、SOCK_SEQPACKETタイプのソケットがnet.ConnインターフェースのNetwork()メソッドで誤ったネットワーク名を返す可能性があったことです。これにより、接続のタイプを正確に識別できず、アプリケーションの動作に予期せぬ影響を与える可能性がありました。

前提知識の解説

Go言語のnetパッケージ

netパッケージは、ネットワークI/Oプリミティブへのポータブルなインターフェースを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルをサポートしています。

  • net.Connインターフェース: ネットワーク接続の汎用インターフェースです。ReadWriteCloseLocalAddrRemoteAddrSetDeadlineなどのメソッドを持ちます。
  • net.Listenerインターフェース: 着信ネットワーク接続をリッスンするための汎用インターフェースです。AcceptAddrCloseなどのメソッドを持ちます。
  • LocalAddr() / RemoteAddr(): net.Connインターフェースのメソッドで、それぞれローカルエンドポイントとリモートエンドポイントのネットワークアドレスを返します。返されるアドレスはnet.Addrインターフェースを実装しています。
  • Network(): net.Addrインターフェースのメソッドで、アドレスのネットワークタイプ(例: "tcp", "udp", "unix", "unixgram", "unixpacket")を文字列で返します。

Unixドメインソケット

Unixドメインソケット(またはIPCソケット)は、同じホスト上のプロセス間通信(IPC)に使用されるエンドポイントです。ネットワークスタックを介さずに直接カーネルを介して通信するため、TCP/IPソケットよりも高速で効率的です。

  • SOCK_STREAM: 信頼性のある、接続指向のバイトストリームを提供します。TCPに似ています。
  • SOCK_DGRAM: 信頼性のない、コネクションレスなデータグラムを提供します。UDPに似ています。
  • SOCK_SEQPACKET: 信頼性のある、接続指向のデータグラムを提供します。メッセージの境界が保持される点でSOCK_STREAMと異なります。

syscallパッケージ

syscallパッケージは、オペレーティングシステムの低レベルなプリミティブへのインターフェースを提供します。ネットワークプログラミングにおいては、ソケットの作成や設定など、より詳細な制御が必要な場合に使用されます。

  • syscall.SOCK_SEQPACKET: syscallパッケージで定義されている定数で、シーケンスパケットソケットのタイプを示します。

sockaddrToUnixsockaddrToUnixpacket

これらはGoの内部的な関数(または型変換ヘルパー)で、Unixソケットのアドレス構造体(sockaddr)をGoのnet.Addrインターフェースに変換する際に使用されます。

  • sockaddrToUnix: 通常のUnixドメインソケット(SOCK_STREAMSOCK_DGRAM)のアドレス変換に使用されます。
  • sockaddrToUnixpacket: SOCK_SEQPACKETタイプのUnixドメインソケットのアドレス変換に特化して使用されます。このコミットの修正の中心となる部分です。

技術的詳細

このコミットの主要な問題は、GoのnetパッケージがUnixドメインソケットのSOCK_SEQPACKETタイプを正しく識別していなかったことにあります。具体的には、UnixListenerAcceptUnix()メソッドで新しい接続を受け入れる際、SOCK_SEQPACKETソケットに対して、誤って通常のUnixソケット用のアドレス変換関数(sockaddrToUnix)を使用していたため、返されるnet.ConnオブジェクトのNetwork()メソッドが"unix"を返すべきところで"unixpacket"を返さないという問題が発生していました。

この修正は、以下の2つの主要な変更によって行われました。

  1. src/pkg/net/unixsock_posix.goの修正: UnixListenerAcceptUnix()メソッド内で、リスナーのソケットタイプがsyscall.SOCK_SEQPACKETである場合、アドレス変換関数としてsockaddrToUnixpacketを使用するように変更されました。これにより、SOCK_SEQPACKETソケットから受け入れられた接続が、正しく"unixpacket"というネットワーク名を報告するようになります。

  2. src/pkg/net/conn_test.goのテスト強化: TestConnAndListener関数に、ln.Addr().Network()c.LocalAddr().Network()c.RemoteAddr().Network()が期待されるネットワーク名(tt.net)を返すことを確認するアサーションが追加されました。これにより、ネットワーク名のハンドリングが正しく行われているかを検証するテストカバレッジが向上しました。特に、transponder関数内でも同様のチェックが追加され、サーバー側で受け入れられた接続のネットワーク名も検証されるようになりました。

これらの変更により、GoのnetパッケージはUnixドメインソケット、特にSOCK_SEQPACKETタイプの接続において、より正確なネットワーク名を提供できるようになり、アプリケーションが接続タイプに基づいて適切な処理を行えるようになりました。

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

src/pkg/net/conn_test.go

--- a/src/pkg/net/conn_test.go
+++ b/src/pkg/net/conn_test.go
@@ -53,7 +53,9 @@ func TestConnAndListener(t *testing.T) {
 			os.Remove(addr)
 		}(ln, tt.net, addr)
-		ln.Addr()
+		if ln.Addr().Network() != tt.net {
+			t.Fatalf("got %v; expected %v", ln.Addr().Network(), tt.net)
+		}
 
 		done := make(chan int)
 		go transponder(t, ln, done)
@@ -63,8 +65,9 @@ func TestConnAndListener(t *testing.T) {
 			t.Fatalf("Dial failed: %v", err)
 		}
 		defer c.Close()
-		c.LocalAddr()
-		c.RemoteAddr()
+		if c.LocalAddr().Network() != tt.net || c.LocalAddr().Network() != tt.net {
+			t.Fatalf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), tt.net, tt.net)
+		}
 		c.SetDeadline(time.Now().Add(someTimeout))
 		c.SetReadDeadline(time.Now().Add(someTimeout))
 		c.SetWriteDeadline(time.Now().Add(someTimeout))
@@ -96,8 +99,11 @@ func transponder(t *testing.T, ln Listener, done chan<- int) {
 		return
 	}
 	defer c.Close()
-	c.LocalAddr()
-	c.RemoteAddr()
+	network := ln.Addr().Network()
+	if c.LocalAddr().Network() != network || c.LocalAddr().Network() != network {
+		t.Errorf("got %v->%v; expected %v->%v", c.LocalAddr().Network(), c.RemoteAddr().Network(), network, network)
+		return
+	}
 	c.SetDeadline(time.Now().Add(someTimeout))
 	c.SetReadDeadline(time.Now().Add(someTimeout))
 	c.SetWriteDeadline(time.Now().Add(someTimeout))

src/pkg/net/unixsock_posix.go

--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -280,7 +280,11 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
 	if l == nil || l.fd == nil {
 		return nil, syscall.EINVAL
 	}
-	fd, err := l.fd.accept(sockaddrToUnix)
+	toAddr := sockaddrToUnix
+	if l.fd.sotype == syscall.SOCK_SEQPACKET {
+		toAddr = sockaddrToUnixpacket
+	}
+	fd, err := l.fd.accept(toAddr)
 	if err != nil {
 		return nil, err
 	}

コアとなるコードの解説

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

このファイルでは、TestConnAndListenerテスト関数が強化されています。

  • リスナーのネットワーク名検証: ln.Addr()が返すnet.AddrNetwork()メソッドが、テストケースで期待されるネットワーク名(tt.net)と一致するかどうかを確認するアサーションが追加されました。これにより、リスナーが正しいネットワークタイプで初期化されていることを保証します。

  • ダイヤルされた接続のネットワーク名検証: net.Dial()で確立されたクライアント側の接続cについて、c.LocalAddr().Network()c.RemoteAddr().Network()が、それぞれ期待されるネットワーク名(tt.net)と一致するかどうかを確認するアサーションが追加されました。これは、クライアント側から見た接続のネットワークタイプが正しいことを検証します。

  • トランスポンダー(サーバー側)のネットワーク名検証: transponder関数は、サーバー側でln.Accept()によって受け入れられた接続を処理します。ここでも、受け入れられた接続cc.LocalAddr().Network()c.RemoteAddr().Network()が、リスナーのネットワーク名(network := ln.Addr().Network()で取得)と一致するかどうかを確認するアサーションが追加されました。これにより、サーバー側で受け入れられた着信接続のネットワークタイプが正しく報告されていることを保証します。

これらのテストの追加により、ネットワーク名のハンドリングに関する回帰バグが将来的に発生するのを防ぐことができます。

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

このファイルは、UnixドメインソケットのPOSIXシステムコール関連の実装を含んでいます。

  • UnixListener.AcceptUnix()メソッドの修正: UnixListenerAcceptUnix()メソッドは、新しいUnixドメインソケット接続を受け入れる際に呼び出されます。 変更前は、l.fd.accept(sockaddrToUnix)というように、常にsockaddrToUnixというアドレス変換関数を使用していました。 変更後は、toAddr := sockaddrToUnixでデフォルト値を設定し、その後に条件分岐が追加されました。 if l.fd.sotype == syscall.SOCK_SEQPACKETという条件で、リスナーのソケットタイプがSOCK_SEQPACKETであるかどうかをチェックします。 もしSOCK_SEQPACKETであれば、toAddrsockaddrToUnixpacketに上書きします。 最終的に、l.fd.accept(toAddr)が呼び出され、適切なアドレス変換関数が使用されるようになります。

この修正により、SOCK_SEQPACKETタイプのUnixドメインソケットからの着信接続が、net.ConnインターフェースのNetwork()メソッドで正しく"unixpacket"というネットワーク名を返すようになります。これは、Goのネットワーク抽象化が、基盤となるソケットタイプを正確に反映するようにするための重要な修正です。

関連リンク

参考にした情報源リンク

  • コミットデータ: ./commit_data/18374.txt
  • Go言語のnetパッケージドキュメント (一般的な情報源として)
  • Unixドメインソケットに関する一般的な知識
  • syscallパッケージに関する一般的な知識