[インデックス 18374] ファイルの概要
このコミットは、Go言語のnet
パッケージにおける、Unixネットワーク上の着信接続のネットワーク名処理に関するバグ修正です。具体的には、src/pkg/net/conn_test.go
とsrc/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
インターフェース: ネットワーク接続の汎用インターフェースです。Read
、Write
、Close
、LocalAddr
、RemoteAddr
、SetDeadline
などのメソッドを持ちます。net.Listener
インターフェース: 着信ネットワーク接続をリッスンするための汎用インターフェースです。Accept
、Addr
、Close
などのメソッドを持ちます。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
パッケージで定義されている定数で、シーケンスパケットソケットのタイプを示します。
sockaddrToUnix
とsockaddrToUnixpacket
これらはGoの内部的な関数(または型変換ヘルパー)で、Unixソケットのアドレス構造体(sockaddr
)をGoのnet.Addr
インターフェースに変換する際に使用されます。
sockaddrToUnix
: 通常のUnixドメインソケット(SOCK_STREAM
やSOCK_DGRAM
)のアドレス変換に使用されます。sockaddrToUnixpacket
:SOCK_SEQPACKET
タイプのUnixドメインソケットのアドレス変換に特化して使用されます。このコミットの修正の中心となる部分です。
技術的詳細
このコミットの主要な問題は、Goのnet
パッケージがUnixドメインソケットのSOCK_SEQPACKET
タイプを正しく識別していなかったことにあります。具体的には、UnixListener
がAcceptUnix()
メソッドで新しい接続を受け入れる際、SOCK_SEQPACKET
ソケットに対して、誤って通常のUnixソケット用のアドレス変換関数(sockaddrToUnix
)を使用していたため、返されるnet.Conn
オブジェクトのNetwork()
メソッドが"unix"を返すべきところで"unixpacket"を返さないという問題が発生していました。
この修正は、以下の2つの主要な変更によって行われました。
-
src/pkg/net/unixsock_posix.go
の修正:UnixListener
のAcceptUnix()
メソッド内で、リスナーのソケットタイプがsyscall.SOCK_SEQPACKET
である場合、アドレス変換関数としてsockaddrToUnixpacket
を使用するように変更されました。これにより、SOCK_SEQPACKET
ソケットから受け入れられた接続が、正しく"unixpacket"というネットワーク名を報告するようになります。 -
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.Addr
のNetwork()
メソッドが、テストケースで期待されるネットワーク名(tt.net
)と一致するかどうかを確認するアサーションが追加されました。これにより、リスナーが正しいネットワークタイプで初期化されていることを保証します。 -
ダイヤルされた接続のネットワーク名検証:
net.Dial()
で確立されたクライアント側の接続c
について、c.LocalAddr().Network()
とc.RemoteAddr().Network()
が、それぞれ期待されるネットワーク名(tt.net
)と一致するかどうかを確認するアサーションが追加されました。これは、クライアント側から見た接続のネットワークタイプが正しいことを検証します。 -
トランスポンダー(サーバー側)のネットワーク名検証:
transponder
関数は、サーバー側でln.Accept()
によって受け入れられた接続を処理します。ここでも、受け入れられた接続c
のc.LocalAddr().Network()
とc.RemoteAddr().Network()
が、リスナーのネットワーク名(network := ln.Addr().Network()
で取得)と一致するかどうかを確認するアサーションが追加されました。これにより、サーバー側で受け入れられた着信接続のネットワークタイプが正しく報告されていることを保証します。
これらのテストの追加により、ネットワーク名のハンドリングに関する回帰バグが将来的に発生するのを防ぐことができます。
src/pkg/net/unixsock_posix.go
の変更
このファイルは、UnixドメインソケットのPOSIXシステムコール関連の実装を含んでいます。
UnixListener.AcceptUnix()
メソッドの修正:UnixListener
のAcceptUnix()
メソッドは、新しいUnixドメインソケット接続を受け入れる際に呼び出されます。 変更前は、l.fd.accept(sockaddrToUnix)
というように、常にsockaddrToUnix
というアドレス変換関数を使用していました。 変更後は、toAddr := sockaddrToUnix
でデフォルト値を設定し、その後に条件分岐が追加されました。if l.fd.sotype == syscall.SOCK_SEQPACKET
という条件で、リスナーのソケットタイプがSOCK_SEQPACKET
であるかどうかをチェックします。 もしSOCK_SEQPACKET
であれば、toAddr
をsockaddrToUnixpacket
に上書きします。 最終的に、l.fd.accept(toAddr)
が呼び出され、適切なアドレス変換関数が使用されるようになります。
この修正により、SOCK_SEQPACKET
タイプのUnixドメインソケットからの着信接続が、net.Conn
インターフェースのNetwork()
メソッドで正しく"unixpacket"というネットワーク名を返すようになります。これは、Goのネットワーク抽象化が、基盤となるソケットタイプを正確に反映するようにするための重要な修正です。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/731e6f7d1d7238c8465f44691836a8a865d4cdb9
- Go Code Review (CL): https://golang.org/cl/57520043
参考にした情報源リンク
- コミットデータ:
./commit_data/18374.txt
- Go言語の
net
パッケージドキュメント (一般的な情報源として) - Unixドメインソケットに関する一般的な知識
syscall
パッケージに関する一般的な知識