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

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

このコミットは、Go言語のnetパッケージにおけるunixgram(Unixドメインデータグラムソケット)の誤った挙動を修正するものです。具体的には、AF_UNIXドメインでSOCK_DGRAMタイプのソケットがlistenシステムコールと連携できないというOSレベルの制約に対応し、ListenUnix関数からunixgramのサポートを削除しています。これにより、netパッケージが提供するAPIと基盤となるOSのソケット挙動との整合性が保たれます。

コミット

commit a0430dae0482f2e9fc90255a568047be78129f13
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Sat Feb 9 08:18:32 2013 +0900

    net: fix unixgram
    
    The socket for AF_UNIX domain with SOCK_DGARM type isn't
    allowed to work with syscall listen.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/7310068

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

https://github.com/golang/go/commit/a0430dae0482f2e9fc90252a568047be78129f13

元コミット内容

net: fix unixgram

AF_UNIXドメインでSOCK_DGRAMタイプのソケットは、listenシステムコールと連携することが許可されていません。

変更の背景

この変更の背景には、Unixドメインソケットの基本的な動作原理と、特定のソケットタイプがサポートする操作に関するOSレベルの制約があります。

Unixドメインソケットには主に2つのタイプがあります。

  1. ストリームソケット (SOCK_STREAM): TCPソケットに似ており、信頼性のある接続指向のデータ転送を提供します。サーバーはlistenシステムコールを使用して接続を待ち受け、クライアントからの接続を受け入れます。
  2. データグラムソケット (SOCK_DGRAM): UDPソケットに似ており、コネクションレスで信頼性のないデータグラム転送を提供します。データグラムソケットは通常、sendtorecvfromといった関数で直接データを送受信し、接続を確立するためのlisten操作は行いません。

コミットメッセージが示唆するように、AF_UNIXドメインのSOCK_DGRAMソケット(Goのnetパッケージではunixgramとして扱われる)は、OSの設計上、listenシステムコールを呼び出すことができません。listenは接続指向のソケット(SOCK_STREAM)が接続要求をキューに入れるために使用されるものであり、データグラムソケットにはその概念がありません。

Goのnetパッケージは、様々なネットワークプロトコルとソケットタイプを抽象化して統一的なAPIを提供していますが、その実装は基盤となるOSのシステムコールに依存しています。このコミット以前は、netパッケージのListenUnix関数が誤ってunixgramネットワークタイプをサポートしていると宣言しており、これによりユーザーがunixgramソケットでlistenを試みると、OSレベルでエラーが発生する可能性がありました。この修正は、GoのAPIがOSの制約を正しく反映するようにするためのものです。

前提知識の解説

Unixドメインソケット (Unix Domain Sockets, UDS)

Unixドメインソケットは、同じホスト上のプロセス間通信 (IPC) のためのメカニズムです。ネットワークソケット(TCP/IPなど)とは異なり、UDSはファイルシステムパス(例: /tmp/mysocket)に関連付けられ、ネットワークスタックを介さずに直接カーネル内で通信が行われるため、TCP/IPソケットよりも高速でオーバーヘッドが少ないという利点があります。

UDSには、ネットワークソケットと同様に、ストリーム型とデータグラム型の2種類があります。

  • ストリーム型 (SOCK_STREAM): 信頼性があり、順序付けされた、接続指向のバイトストリームを提供します。TCPに似ています。
  • データグラム型 (SOCK_DGRAM): コネクションレスで、信頼性のないデータグラムを提供します。UDPに似ています。各メッセージは独立したパケットとして送信されます。

listen システムコール

listenシステムコールは、ソケットが着信接続を受け入れる準備ができていることをマークするために使用されます。これは通常、サーバーアプリケーションがクライアントからの接続を待ち受ける際に、bind(ソケットにアドレスを割り当てる)とaccept(接続を受け入れる)の間に呼び出されます。listenは接続指向のソケット(SOCK_STREAM)にのみ適用され、データグラムソケット(SOCK_DGRAM)には適用されません。データグラムソケットは接続の概念を持たないため、listenを呼び出すことはできません。

Go言語の net パッケージ

Go言語の標準ライブラリnetパッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなど、様々なネットワークプロトコルを扱うためのインターフェースが含まれています。

  • net.Dial: ネットワーク接続を確立します。
  • net.Listen: ネットワークアドレスで着信接続を待ち受けます。
  • net.UnixConn: Unixドメインソケット接続を表します。
  • net.UnixListener: Unixドメインソケットのリスナーを表します。

技術的詳細

このコミットの技術的詳細は、Unixドメインデータグラムソケット(unixgram)がlistenシステムコールをサポートしないというOSの制約に起因します。

  1. SOCK_DGRAMlistenの非互換性: データグラムソケットは、メッセージの送受信に特化しており、接続の概念を持ちません。そのため、接続を待ち受けるためのlistenシステムコールは、データグラムソケットに対しては意味をなさず、通常はEOPNOTSUPP(Operation not supported)のようなエラーを返します。

  2. Goのnetパッケージの抽象化とOSの制約: Goのnetパッケージは、異なるOSやソケットタイプ間の差異を吸収し、統一されたAPIを提供しようとします。しかし、基盤となるOSの制約を完全に無視することはできません。この場合、net.ListenUnix関数がunixgramをサポートすると宣言していたことが問題でした。これは、GoのAPIがOSの実際の挙動と乖離していたことを意味します。

  3. 修正のロジック: コミットは、この乖離を修正するために、ListenUnix関数がunixgramネットワークタイプを受け入れないように変更しています。これにより、ユーザーがunixgramlistenを試みることを防ぎ、実行時エラーを未然に防ぎます。

    • src/pkg/net/dial.go: resolveAddr関数のコメントからunixgramListen可能なネットワークタイプとして誤って記載されていた部分を削除。これはドキュメントの修正です。
    • src/pkg/net/unixsock_plan9.goおよびsrc/pkg/net/unixsock_posix.go: ListenUnix関数の実装において、net引数のswitch文から"unixgram"ケースを削除。これにより、ListenUnix"unixgram"を受け取った場合にUnknownNetworkErrorを返すようになります。

この修正により、Goのnetパッケージは、Unixドメインデータグラムソケットの正しい使用法(listenではなく直接送受信)を強制し、開発者がOSの制約に起因する予期せぬエラーに遭遇するのを防ぎます。

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

このコミットでは、以下の3つのファイルが変更されています。

  1. src/pkg/net/dial.go

    • resolveAddr関数のコメントから、ListenUnixがサポートするネットワークタイプとして"unixgram"が削除されました。これはドキュメントの修正です。
    --- a/src/pkg/net/dial.go
    +++ b/src/pkg/net/dial.go
    @@ -54,7 +54,8 @@ func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
     //
     // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
     // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
    -// (IPv4-only), "ip6" (IPv6-only), "unix" and "unixpacket".
    +// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
    +// "unixpacket".
     //
     // For TCP and UDP networks, addresses have the form host:port.
     // If host is a literal IPv6 address, it must be enclosed
    

    : 上記のdiffは、変更前のコードがunixgramを含んでいたことを示しており、変更によってunixgramが削除されたことを意味します。元のコミットメッセージのdiff表示と実際の変更内容が逆になっているように見えますが、これはdial.goのコメントがunixgramListen可能なネットワークとして誤ってリストしていたのを修正したものです。つまり、unixgramをリストから削除する変更です。

  2. src/pkg/net/unixsock_plan9.go

    • ListenUnix関数のコメントから、"unixgram"がサポートされるネットワークタイプとして削除されました。
    • ListenUnixの実装自体は、Plan 9ではsyscall.EPLAN9を返すため、具体的なswitch文の変更はありませんが、コメントの修正が重要です。
    --- a/src/pkg/net/unixsock_plan9.go
    +++ b/src/pkg/net/unixsock_plan9.go
    @@ -93,8 +93,7 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
     type UnixListener struct{}
      
     // ListenUnix announces on the Unix domain socket laddr and returns a
    -// Unix listener.  The network net must be "unix", "unixgram" or
    -// "unixpacket".
    +// Unix listener.  The network net must be "unix" or "unixpacket".
     func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
     	return nil, syscall.EPLAN9
     }
    
  3. src/pkg/net/unixsock_posix.go

    • ListenUnix関数のコメントから、"unixgram"がサポートされるネットワークタイプとして削除されました。
    • ListenUnixの実装内のswitch net文から"unixgram"ケースが削除されました。これにより、"unixgram"が渡された場合にUnknownNetworkErrorが返されるようになります。
    --- a/src/pkg/net/unixsock_posix.go
    +++ b/src/pkg/net/unixsock_posix.go
    @@ -251,11 +251,10 @@ type UnixListener struct {
     }
      
     // ListenUnix announces on the Unix domain socket laddr and returns a
    -// Unix listener.  The network net must be "unix", "unixgram" or
    -// "unixpacket".
    +// Unix listener.  The network net must be "unix" or "unixpacket".
     func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
     	switch net {
    -\tcase "unix", "unixgram", "unixpacket":
    +\tcase "unix", "unixpacket":
     	default:
     		return nil, UnknownNetworkError(net)
     	}
    

コアとなるコードの解説

このコミットの核心は、netパッケージのListenUnix関数が、Unixドメインデータグラムソケット(unixgram)に対してlisten操作を許可しないように変更された点です。

ListenUnix関数は、指定されたネットワークタイプ(net引数)とローカルアドレス(laddr引数)に基づいてUnixドメインソケットのリスナーを作成します。変更前は、この関数が"unixgram"タイプも有効なネットワークとして受け入れていました。しかし、前述の通り、データグラムソケットはlistenシステムコールをサポートしません。

src/pkg/net/unixsock_posix.goの変更が最も重要です。このファイルはPOSIX準拠システム(Linux, macOSなど)におけるUnixソケットの実装を含んでいます。

func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
	switch net {
	case "unix", "unixpacket": // 変更点: "unixgram" が削除された
	default:
		return nil, UnknownNetworkError(net)
	}
	// ... 以降のリスナー作成ロジック
}

このswitch文は、ListenUnix関数がサポートするネットワークタイプをチェックしています。変更前は"unix", "unixgram", "unixpacket"の3つを許可していましたが、このコミットにより"unixgram"が削除されました。

これにより、ユーザーがListenUnix("unixgram", ...)を呼び出すと、defaultケースにフォールバックし、UnknownNetworkErrorが返されるようになります。これは、unixgramソケットでlistenを試みることがOSレベルで無効であるという事実を、GoのAPIレベルで強制するものです。

src/pkg/net/dial.gosrc/pkg/net/unixsock_plan9.goにおけるコメントの修正は、このAPIの変更をドキュメントに反映させるためのものです。特にdial.goのコメントは、netパッケージが認識するネットワークタイプの一覧を更新し、unixgramListen操作には適さないことを明確にしています。

この修正は、Goのnetパッケージの堅牢性を高め、開発者がOSのソケットプログラミングの基本的な制約を誤解することなく、正しい方法でUnixドメインソケットを扱えるようにするために不可欠です。

関連リンク

参考にした情報源リンク