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

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

このコミットは、Go言語のnetパッケージにおけるDialTimeout関数のテストの信頼性を向上させるための変更です。具体的には、テストで使用されるポート番号を、以前の「適当なポート番号(44444)」から、IANA(Internet Assigned Numbers Authority)によって予約されているポート番号(49151)に変更することで、テストが意図しない接続を確立してしまう問題を解決しています。これにより、DialTimeoutが実際にタイムアウトすることを確実に検証できるようになります。

コミット

  • コミットハッシュ: ae7a84347100f375f9c7ba01cf042faf4e9fcc1a
  • Author: Mikio Hara mikioh.mikioh@gmail.com
  • Date: Wed Mar 7 16:28:40 2012 +0900
  • コミットメッセージ:
    net: use IANA reserved port to test dial timeout
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5757060
    

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

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

元コミット内容

net: use IANA reserved port to test dial timeout

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5757060
---
 src/pkg/net/dial_test.go | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go
index f9c47d02bb..7212087fe0 100644
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -51,12 +51,15 @@ func TestDialTimeout(t *testing.T) {
 		// to connecting to a hopefully-dead 127/8 address.
 		// Same for windows.
 		//
-\t\t// Use a bogus port (44444) instead of 80, because
+\t\t// Use an IANA reserved port (49151) instead of 80, because
 \t\t// on our 386 builder, this Dial succeeds, connecting
 \t\t// to an IIS web server somewhere.  The data center
 \t\t// or VM or firewall must be stealing the TCP connection.
+\t\t// 
+\t\t// IANA Service Name and Transport Protocol Port Number Registry
+\t\t// <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
 \t\tgo func() {\n-\t\t\tc, err := DialTimeout(\"tcp\", \"127.0.71.111:44444\", 200*time.Millisecond)\n+\t\t\tc, err := DialTimeout(\"tcp\", \"127.0.71.111:49151\", 200*time.Millisecond)\n \t\t\tif err == nil {\n \t\t\t\terr = fmt.Errorf(\"unexpected: connected to %s!\", c.RemoteAddr())\n \t\t\t\tc.Close()\n```

## 変更の背景

Go言語の`net`パッケージには、指定されたタイムアウト期間内にネットワーク接続を確立しようとする`DialTimeout`関数があります。この関数のテストでは、意図的に存在しない(または応答しない)アドレスとポートに接続を試み、タイムアウトが正しく機能することを確認する必要があります。

しかし、以前のテストコードでは、ループバックアドレス(`127.0.71.111`)と「適当なポート番号」として`44444`番ポートを使用していました。この設定には問題がありました。コミットメッセージによると、特定の環境(特に386アーキテクチャのビルド環境)において、この`Dial`が成功し、IISウェブサーバーに接続してしまう事象が発生していました。これは、データセンター、仮想マシン、またはファイアウォールがTCP接続を「奪い」、テストが意図しない成功を収めてしまうためでした。

このような状況では、`DialTimeout`が実際にタイムアウトするべきシナリオでタイムアウトせず、テストが誤って成功と判断されてしまいます。これはテストの信頼性を著しく損なうため、確実に接続が確立されないポートを使用する必要がありました。

## 前提知識の解説

### Go言語の`net`パッケージ

Go言語の標準ライブラリである`net`パッケージは、ネットワークI/Oのプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルを扱うための機能が含まれており、クライアントやサーバーアプリケーションを構築する上で不可欠なパッケージです。

### `DialTimeout`関数

`net`パッケージの`DialTimeout`関数は、ネットワーク接続を確立するための関数です。通常の`Dial`関数と異なり、接続試行にタイムアウト期間を設定できます。指定されたタイムアウト期間内に接続が確立できない場合、関数はエラーを返します。これは、ネットワークの遅延や応答しないサーバーによってアプリケーションがブロックされるのを防ぐために非常に重要です。

### TCP/IP

TCP/IP(Transmission Control Protocol/Internet Protocol)は、インターネットを含む多くのネットワークで利用されている通信プロトコル群です。TCPは信頼性の高いデータ転送を提供し、接続指向型です。IPはデータのルーティングを担当します。TCP接続を確立する際には、クライアントはサーバーのIPアドレスとポート番号を指定して接続要求を送信します。

### IANA (Internet Assigned Numbers Authority)

IANAは、IPアドレス、AS番号、ドメイン名、プロトコルポート番号など、インターネットプロトコルに関連するグローバルな識別子を調整する責任を負う組織です。IANAは、これらの識別子の一意性とグローバルな到達性を確保し、インターネットの安定した運用を支えています。

### IANA予約済みポート (Reserved Ports)

TCP/UDPポート番号は0から65535まであります。IANAはこれらのポート番号を以下の3つの範囲に分類しています。

1.  **Well-Known Ports (0-1023)**: HTTP (80), HTTPS (443), FTP (21), SSH (22) など、特定のサービスに割り当てられたポート。
2.  **Registered Ports (1024-49151)**: 特定のアプリケーションやサービスがIANAに登録して使用するポート。
3.  **Dynamic/Private/Ephemeral Ports (49152-65535)**: どのサービスにも割り当てられていない、一時的な接続のためにクライアントが自由に使用できるポート。これらのポートは「予約済みポート」とも呼ばれ、特定のサービスに恒久的に割り当てられることはありません。

このコミットで言及されている`49151`は、Registered Portsの範囲の最上位に位置し、Dynamic/Private/Ephemeral Portsの範囲の直前です。このポートはIANAに登録されていますが、特定の一般的なサービスに広く使用されているわけではありません。

### `127.0.0.0/8` (ループバックアドレス)

`127.0.0.0`から`127.255.255.255`までのIPアドレス範囲は、ループバックアドレスとして予約されています。これは、ネットワークインターフェースを介さずに、同じホスト内のプロセス間で通信を行うために使用されます。最も一般的なのは`127.0.0.1`で、「localhost」として知られています。このアドレスへの接続は、外部ネットワークには出ず、常に自分自身に戻ってきます。テストでは、外部のネットワーク環境に依存せずに、ローカルで接続試行の挙動を検証するために使用されます。

## 技術的詳細

`DialTimeout`のテストでは、接続が成功しないことを確認することが重要です。そのためには、以下の条件を満たすアドレスとポートの組み合わせが必要です。

1.  **到達可能なIPアドレス**: ループバックアドレス(`127.0.71.111`)を使用することで、ネットワークルーティングの問題を排除し、接続試行がローカルホスト内で完結するようにします。
2.  **応答しないポート**: これが今回の変更の核心です。以前使用されていた`44444`番ポートは、IANAのRegistered Portsの範囲にありますが、特定の環境下でIISウェブサーバーなどのサービスがこのポートをリッスンしている可能性がありました。これは、データセンターのネットワーク構成、仮想化環境のNAT設定、またはファイアウォールによるポート転送など、予期せぬ要因によって発生し得ます。テストが意図しない接続を確立してしまうと、`DialTimeout`がタイムアウトするべきシナリオでタイムアウトせず、テストの目的が達成されません。

この問題を解決するため、コミットでは`49151`番ポートが選択されました。このポートはIANAのRegistered Portsの範囲の終端に近く、一般的に広く使用されるサービスには割り当てられていません。また、Dynamic/Private/Ephemeral Portsの範囲(49152-65535)の直前であるため、一時的な接続にも使用されにくいという特性があります。

テストのコメントで言及されているように、IANAの「Service Name and Transport Protocol Port Number Registry」を参照することで、ポート番号の割り当て状況を確認できます。`49151`番ポートはIANAに登録されていますが、特定のサービスに広く使われているわけではないため、テスト目的で「応答しないポート」として使用するのに適していると判断されました。これにより、テスト環境に依存せず、`DialTimeout`が確実にタイムアウトする状況を作り出すことが可能になります。

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

変更は`src/pkg/net/dial_test.go`ファイルの一箇所のみです。

```diff
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -51,12 +51,15 @@ func TestDialTimeout(t *testing.T) {
 		// to connecting to a hopefully-dead 127/8 address.
 		// Same for windows.
 		//
-\t\t// Use a bogus port (44444) instead of 80, because
+\t\t// Use an IANA reserved port (49151) instead of 80, because
 \t\t// on our 386 builder, this Dial succeeds, connecting
 \t\t// to an IIS web server somewhere.  The data center
 \t\t// or VM or firewall must be stealing the TCP connection.
+\t\t// 
+\t\t// IANA Service Name and Transport Protocol Port Number Registry
+\t\t// <http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml>
 \t\tgo func() {\n-\t\t\tc, err := DialTimeout(\"tcp\", \"127.0.71.111:44444\", 200*time.Millisecond)\n+\t\t\tc, err := DialTimeout(\"tcp\", \"127.0.71.111:49151\", 200*time.Millisecond)\n \t\t\tif err == nil {\n \t\t\t\terr = fmt.Errorf(\"unexpected: connected to %s!\", c.RemoteAddr())\n \t\t\t\tc.Close()\

コアとなるコードの解説

変更は以下の2点です。

  1. ポート番号の変更:

    • 変更前: DialTimeout("tcp", "127.0.71.111:44444", 200*time.Millisecond)
    • 変更後: DialTimeout("tcp", "127.0.71.111:49151", 200*time.Millisecond) DialTimeout関数に渡すアドレス文字列のポート部分が44444から49151に変更されました。これにより、テストが接続を試みるターゲットポートが更新され、意図しない接続成功を防ぎます。
  2. コメントの追加と修正:

    • 既存のコメント// Use a bogus port (44444) instead of 80, because// Use an IANA reserved port (49151) instead of 80, becauseに修正されました。これにより、新しいポートが単なる「適当なポート」ではなく、「IANA予約済みポート」であることが明確に示されます。
    • さらに、IANAの「Service Name and Transport Protocol Port Number Registry」へのURLがコメントとして追加されました。これは、なぜこのポートが選ばれたのか、その根拠を明確にするためのものです。このリンクは、ポート番号の割り当てに関する公式な情報源を示しており、将来のコードレビューやメンテナンスにおいて、このポート選択の意図を理解するのに役立ちます。

これらの変更により、DialTimeoutのテストはより堅牢になり、様々な環境下で一貫してタイムアウトの挙動を正確に検証できるようになりました。

関連リンク

参考にした情報源リンク