[インデックス 10077] ファイルの概要
このコミットは、Go言語のネットワークパッケージにおいて、Windows環境でのソケットオプション SO_REUSEADDR
の設定を削除するものです。これにより、Windowsにおける SO_REUSEADDR
の挙動に起因する潜在的な問題が回避されます。
コミット
- コミットハッシュ:
c1d0f0e76c4294abc1eb8464c06fb329717a7cdb
- 作者: Alex Brainman alex.brainman@gmail.com
- コミット日時: Mon Oct 24 09:11:01 2011 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c1d0f0e76c4294abc1eb8464c06fb329717a7cdb
元コミット内容
net: do not set SO_REUSEADDR for windows
Fixes #2307.
R=golang-dev, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5306049
変更の背景
このコミットの背景には、SO_REUSEADDR
ソケットオプションがWindowsとUnix系OS(Linuxなど)で異なる挙動を示すという重要な技術的差異があります。
Go言語のネットワークパッケージは、クロスプラットフォームで動作するように設計されています。しかし、SO_REUSEADDR
の挙動の違いがWindows環境で問題を引き起こしていました。具体的には、Unix系OSでは SO_REUSEADDR
は主に TIME-WAIT
状態にあるソケットのアドレスとポートの再利用を許可するために使用されますが、Windowsではより広範な意味を持ちます。Windowsでは、SO_REUSEADDR
を設定すると、既に別のプロセスによって使用されているアドレスとポートに新しいソケットがバインドできてしまう可能性があります。これは「ポートの乗っ取り(port stealing)」として知られ、セキュリティ上の脆弱性や予期せぬ動作の原因となることがあります。
この問題は、GoのIssue #2307として報告されており、このコミットはその問題を解決するために sock_windows.go
から SO_REUSEADDR
の設定を削除しています。
前提知識の解説
ソケットオプション SO_REUSEADDR
SO_REUSEADDR
は、ソケットがバインドするアドレスとポートの再利用を制御するためのソケットオプションです。
-
Unix系OS (Linux, macOSなど) での挙動:
TIME-WAIT
状態のポートの再利用: 主に、サーバーアプリケーションがシャットダウンされた後、そのソケットがTIME-WAIT
状態にある間に、同じアドレスとポートにすぐに再バインドできるようにするために使用されます。TIME-WAIT
状態は、TCP接続が終了した後、ネットワーク上の遅延パケットが到着するのを待つために一定期間(通常は数分)維持される状態です。SO_REUSEADDR
がないと、この期間中は同じポートにバインドできません。- 異なるプロセスによる同時バインドの防止:
SO_REUSEADDR
が設定されていても、通常、異なる2つのプロセスが全く同じIPアドレスとポートの組み合わせに同時にバインドしてリッスンすることはできません。 - ワイルドカードアドレスの扱い:
0.0.0.0
のようなワイルドカードアドレスの扱いにも影響します。SO_REUSEADDR
が設定されている場合、0.0.0.0:port
にバインドした後で特定のIPアドレス (192.168.0.1:port
など) にバインドすることも可能になります。 SO_REUSEPORT
: Linuxカーネル3.9以降では、SO_REUSEPORT
というオプションが導入されました。これは、複数のプロセスが同じアドレスとポートにバインドすることを明示的に許可し、ロードバランシングなどに利用されます。ただし、ポートハイジャックを防ぐためのセキュリティメカニズムも備わっています。
-
Windows での挙動:
- アドレスの「乗っ取り」: Windowsにおける
SO_REUSEADDR
は、Unix系OSよりもはるかに広範な意味を持ちます。これを設定すると、既に別のプロセスによって使用されているアドレスとポートに、新しいソケットがバインドできてしまいます。これは、意図しないアプリケーションがサービスを乗っ取ったり、セキュリティ上の問題を引き起こしたりする可能性があります。 SO_EXCLUSIVEADDRUSE
: Windowsでは、Unix系OSのSO_REUSEADDR
に近い、より厳密なバインド動作を実現するためにSO_EXCLUSIVEADDRUSE
オプションが提供されています。このオプションを設定してソケットが正常にバインドされると、他のどのソケットも同じアドレスとポートにバインドできなくなります。SO_REUSEPORT
の欠如: WindowsにはSO_REUSEPORT
に相当するオプションはありません。
- アドレスの「乗っ取り」: Windowsにおける
TIME-WAIT
状態
TCP接続が正常に終了する際に、最後に接続を閉じた側(通常はクライアントだが、サーバーの場合もある)のソケットが移行する状態です。この状態は、ネットワーク上で遅延しているパケット(例えば、FINやACKの再送)が到着するのを待ち、それらを適切に処理するために存在します。TIME-WAIT
状態のソケットは、一定期間(通常は2MSL: Maximum Segment Lifetime、最大セグメント寿命の2倍)ポートを占有します。この期間中に同じポートに新しいソケットをバインドしようとすると、通常は「Address already in use」エラーが発生します。
技術的詳細
Go言語のネットワークパッケージは、サーバーアプリケーションを構築する際によく利用されます。サーバーは通常、特定のポートにバインドしてクライアントからの接続を待ち受けます。この際、サーバーが再起動されることを考慮すると、TIME-WAIT
状態のポートを再利用できる SO_REUSEADDR
はUnix系OSでは非常に便利です。
しかし、Windows環境で SO_REUSEADDR
を無条件に設定することは、以下のような問題を引き起こす可能性があります。
- ポートの乗っ取り: 悪意のある、または単に設定ミスのある別のアプリケーションが、既にGoアプリケーションが使用しているポートにバインドできてしまう可能性があります。これにより、正当なサービスが妨害されたり、データが傍受されたりするリスクが生じます。
- 予期せぬ動作: 開発者が意図しない形で、複数のアプリケーションが同じポートを共有してしまうことで、デバッグが困難な問題や、サービスの不安定化を招く可能性があります。
- セキュリティリスク: 特に、特権の低いプロセスが特権の高いプロセスが使用しているポートを乗っ取ることが可能になる場合、深刻なセキュリティ脆弱性につながります。
このコミットは、GoのネットワークパッケージがWindows上でソケットを作成する際に、デフォルトで SO_REUSEADDR
を設定しないようにすることで、これらの問題を回避しています。これにより、Windowsのデフォルトのソケットバインド動作が尊重され、より安全で予測可能な挙動が保証されます。もしWindows上で TIME-WAIT
状態のポートを再利用したい場合は、アプリケーションレベルで SO_EXCLUSIVEADDRUSE
を使用するなど、Windows固有のAPIを明示的に利用する必要があります。
コアとなるコードの変更箇所
変更は src/pkg/net/sock_windows.go
ファイルの1箇所のみです。
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -11,9 +11,6 @@ import (
)
func setKernelSpecificSockopt(s syscall.Handle, f int) {
- // Allow reuse of recently-used addresses and ports.
- syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
-
// Allow broadcast.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
コアとなるコードの解説
src/pkg/net/sock_windows.go
ファイル内の setKernelSpecificSockopt
関数は、Windows環境でソケットにカーネル固有のソケットオプションを設定するために使用されます。
このコミットでは、以下の3行が削除されています。
// Allow reuse of recently-used addresses and ports.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
// Allow reuse of recently-used addresses and ports.
:これは、削除されるコード行の目的を説明するコメントです。syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
:この行が実際にSO_REUSEADDR
オプションをソケットに設定していました。s
: ソケットのハンドル。syscall.SOL_SOCKET
: ソケットレベルのオプションを指定するための定数。syscall.SO_REUSEADDR
: 設定するソケットオプションの名前。1
: オプションの値をtrue
(有効) に設定。
この変更により、Windows上でGoのネットワークパッケージがソケットを作成する際に、デフォルトで SO_REUSEADDR
が設定されなくなります。これにより、前述のWindowsにおける SO_REUSEADDR
の挙動に起因する問題が回避されます。SO_BROADCAST
の設定は引き続き行われますが、これはブロードキャスト通信を許可するためのものであり、SO_REUSEADDR
とは異なる目的を持ちます。
関連リンク
- Go Issue #2307: https://github.com/golang/go/issues/2307
- Go CL (Change List) 5306049: https://golang.org/cl/5306049
参考にした情報源リンク
- geeksforgeeks.org: https://www.geeksforgeeks.org/so_reuseaddr-socket-option-in-linux/
- liberatedsystems.co.uk: https://www.liberatedsystems.co.uk/linux-so_reuseaddr-and-so_reuseport/
- python.org: https://docs.python.org/3/library/socket.html#socket.socket.setsockopt
- winehq.org: https://wiki.winehq.org/SO_REUSEADDR
- stackoverflow.com: https://stackoverflow.com/questions/14388706/what-is-the-difference-between-so-reuseaddr-and-so-exclusiveaddruse
- ziggit.dev: https://ziggit.dev/p/so_reuseaddr-and-so_reuseport-explained
- github.com: https://github.com/golang/go/issues/2307