[インデックス 15038] ファイルの概要
コミット
commit ad341843a7661e4883c8a66b12433e55db1dfc7e
Author: Michael Teichgräber <mteichgraeber@gmx.de>
Date: Wed Jan 30 09:25:16 2013 -0800
net: SplitHostPort: adjust error message for missing port in IPv6 addresses
An hostport of "[::1]" now results in the same error message
"missing port in address" as the hostport value "127.0.0.1",
so SplitHostPort won't complain about "too many colons
in address" anymore for an IPv6 address missing a port.
Added tests checking the error values.
Fixes #4526.
R=dave, rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/7038045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ad341843a7661e4883c8a66b12433e55db1dfc7e
元コミット内容
このコミットは、Go言語のnet
パッケージにおけるSplitHostPort
関数のエラーメッセージの挙動を調整するものです。具体的には、ポート番号が欠落しているIPv6アドレス(例: [::1]
)に対して、これまでは「too many colons in address」(アドレスにコロンが多すぎる)という誤解を招くエラーメッセージを返していた問題を修正し、IPv4アドレス(例: 127.0.0.1
)と同様に「missing port in address」(アドレスにポートがありません)という、より適切なエラーメッセージを返すように変更しています。
この変更には、エラー値を確認する新しいテストケースの追加も含まれています。これにより、Go 1.0では失敗しなかった特定の不正なアドレス形式に対する挙動も改善されています。
変更の背景
Go言語のnet
パッケージには、ホストとポートの文字列を分離するSplitHostPort
関数が存在します。この関数は、ネットワークアドレスの解析において非常に重要な役割を担っています。
従来のSplitHostPort
の実装では、IPv6アドレスの特殊な形式が原因で、ポートが指定されていない場合に不適切なエラーメッセージを返す問題がありました。IPv6アドレスは複数のコロン(:
)を含むため、ポート番号が欠落している場合でも、関数がアドレス内のコロンの数を誤って解釈し、「too many colons in address」というエラーを返していました。これは、IPv4アドレスではコロンがポートセパレータとしてのみ使用されるため、コロンが複数ある場合は不正な形式と判断されるのに対し、IPv6アドレスではアドレス自体にコロンが含まれるという違いに起因します。
この挙動は、ユーザーや開発者にとって混乱を招くものであり、真の問題(ポートの欠落)を正確に伝えるものではありませんでした。GitHub Issue #4526で報告されたこの問題に対処するため、SplitHostPort
関数がIPv6アドレスの解析をより適切に行い、ポートが欠落している場合には一貫して「missing port in address」というエラーを返すように修正されました。これにより、エラーメッセージの正確性が向上し、デバッグが容易になります。
前提知識の解説
1. ホストとポート (Host and Port)
ネットワーク通信において、ホストは通信相手のコンピュータやデバイスを識別するもので、IPアドレス(例: 192.168.1.1
や::1
)やホスト名(例: www.example.com
)で表されます。ポートは、そのホスト上で動作している特定のアプリケーションやサービスを識別するための番号です。例えば、HTTP通信は通常ポート80、HTTPSはポート443を使用します。
ホストとポートは通常、ホスト:ポート
という形式で結合されます。例: 127.0.0.1:8080
、www.google.com:443
。
2. IPv4アドレスとIPv6アドレス
- IPv4アドレス: 32ビットの数値で構成され、通常はドットで区切られた4つの10進数(例:
192.168.1.1
)で表現されます。 - IPv6アドレス: 128ビットの数値で構成され、通常はコロンで区切られた8つの16進数グループ(例:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
)で表現されます。IPv6アドレスは、その性質上、複数のコロンを含みます。また、IPv6アドレスをホストとして使用する場合、曖昧さを避けるために角括弧([]
)で囲むのが一般的です。例:[::1]:8080
。
3. SplitHostPort
関数
Go言語のnet
パッケージに含まれるSplitHostPort
関数は、ホスト:ポート
形式の文字列を受け取り、ホスト部分とポート部分に分割して返します。この関数は、ネットワーク接続を確立する際のアドレス解析において頻繁に利用されます。
4. エラーハンドリングとエラーメッセージ
プログラミングにおいて、エラーハンドリングは非常に重要です。特に、ユーザーからの入力や外部システムからのデータを受け取る際には、予期せぬ形式や値に対して適切に対処する必要があります。エラーメッセージは、何が問題であったかをユーザーや開発者に正確に伝えるための重要な情報源です。不正確なエラーメッセージは、問題の特定と解決を困難にします。
5. Go言語のAddrError
Go言語のnet
パッケージでは、ネットワークアドレスに関連するエラーを表すためにAddrError
という構造体が定義されています。この構造体は、エラーの種類を示す文字列(Err
フィールド)と、エラーが発生したアドレス文字列(Addr
フィールド)を含みます。このコミットでは、AddrError
のErr
フィールドの値が適切に設定されるように修正されています。
技術的詳細
このコミットの核心は、src/pkg/net/ipsock.go
内のsplitHostPort
関数のロジック変更にあります。この関数は、SplitHostPort
から内部的に呼び出され、実際のホストとポートの分離処理を行います。
変更前は、splitHostPort
関数はまず文字列の最後のコロン(:
)を探し、それより前をホスト、後をポートと仮定していました。IPv6アドレスが角括弧で囲まれている場合(例: [::1]:80
)、関数は角括弧を取り除いてからホスト部分を処理していました。しかし、ポートが欠落しているIPv6アドレス(例: [::1]
)の場合、最後のコロンが見つからないため、missing port in address
エラーを返すはずでした。
問題は、角括弧で囲まれていないIPv6アドレス(例: ::1
)や、不正な形式のIPv6アドレスが与えられた場合に発生していました。これらのケースでは、アドレス内に複数のコロンが存在するため、splitHostPort
は「too many colons in address」というエラーを返していました。これは、IPv6アドレスの構造を考慮せず、IPv4アドレスの解析ロジックを単純に適用した結果でした。
今回の修正では、splitHostPort
関数は以下の点を改善しています。
- IPv6アドレスの角括弧の扱い:
- 入力文字列が
[
で始まり、]
で終わる場合、IPv6アドレスとして特別に処理されます。 ]
の位置が最後のコロンの直前にあることを確認し、正しい形式のIPv6アドレスとポートの組み合わせを識別します。]
の後にコロンが続かない場合、または]
の後に続くコロンが最後のコロンでない場合、missing port in address
またはtoo many colons
の適切なエラーに分岐します。
- 入力文字列が
- コロンの数のチェックの改善:
- ホスト部分にコロンが複数含まれる場合のチェックが、IPv6アドレスの角括弧の有無に応じて適切に調整されました。
- これにより、
[::1]
のようにポートが欠落しているIPv6アドレスが、too many colons
ではなくmissing port
として正しく扱われるようになります。
- 不正な角括弧の検出:
- ホスト部分の解析中に、予期せぬ
[
や]
が出現した場合に、それぞれunexpected '[' in address
やunexpected ']' in address
というエラーを返すようになりました。これにより、より具体的なエラー情報が提供されます。
- ホスト部分の解析中に、予期せぬ
goto
ステートメントの導入:- エラー処理のロジックを簡潔にするために、
missingPort
とtooManyColons
というラベルへのgoto
ステートメントが導入されました。これにより、複数の場所から共通のエラー処理ロジックにジャンプできるようになり、コードの重複が削減されています。
- エラー処理のロジックを簡潔にするために、
これらの変更により、SplitHostPort
はIPv6アドレスの解析においてより堅牢になり、ユーザーにとってより分かりやすいエラーメッセージを提供するようになりました。
コアとなるコードの変更箇所
src/pkg/net/ip_test.go
splitfailuretests
という新しいテストケースのスライスが追加されました。このスライスは、SplitHostPort
関数が特定の不正なホストポート文字列に対して期待されるエラーメッセージを返すことを検証します。- 特に、
"[::1]"
に対して"missing port in address"
が期待されるようになりました。 - Go 1.0では失敗しなかったが、この修正で失敗するようになるケース(例:
"[foo:bar]"
)も追加されています。
- 特に、
TestSplitHostPort
関数内に、splitfailuretests
をイテレートし、SplitHostPort
が期待されるエラーを返すかどうかをチェックする新しいループが追加されました。
src/pkg/net/ipsock.go
splitHostPort
関数の実装が大幅に変更されました。j, k := 0, 0
という変数が追加され、文字列の走査範囲を管理するために使用されます。- IPv6アドレスの角括弧(
[
と]
)の処理ロジックが強化されました。hostport[0] == '['
の場合の分岐が追加され、]
の位置とそれに続く文字(コロンの有無)を詳細にチェックします。end + 1
がlen(hostport)
と等しい場合(]
の後に何も続かない場合)はmissingPort
にジャンプします。end + 1
がi
と等しい場合(]
の直後に最後のコロンが続く場合)は正常なパスとして処理されます。- それ以外の場合(
]
の後にコロンが続かない、またはコロンが最後のコロンでない場合)は、tooManyColons
またはmissingPort
にジャンプします。
- 角括弧がない場合のホスト部分のコロンチェックも調整されました。
byteIndex(hostport[j:], '[' ) >= 0
とbyteIndex(hostport[k:], ']' ) >= 0
というチェックが追加され、予期せぬ位置に角括弧がある場合にエラーを返すようになりました。- エラー処理のために
goto
ステートメントが導入され、missingPort
とtooManyColons
というラベルが定義されました。これにより、エラーメッセージの生成ロジックが一元化されました。
コアとなるコードの解説
src/pkg/net/ip_test.go
の変更
var splitfailuretests = []struct {
HostPort string
Err string
}{
{"www.google.com", "missing port in address"},
{"127.0.0.1", "missing port in address"},
{"[::1]", "missing port in address"}, // この行が重要
{"::1", "too many colons in address"}, // この行も重要
// Test cases that didn't fail in Go 1.0
{"[foo:bar]", "missing port in address"},
{"[foo:bar]baz", "missing port in address"},
{"[foo]:[bar]:baz", "too many colons in address"},
{"[foo]bar:baz", "missing port in address"},
{"[foo]:[bar]baz", "unexpected '[' in address"},
{"foo[bar]:baz", "unexpected '[' in address"},
{"foo]bar:baz", "unexpected ']' in address"},
}
func TestSplitHostPort(t *testing.T) {
// ... 既存の成功テスト ...
for _, tt := range splitfailuretests {
if _, _, err := SplitHostPort(tt.HostPort); err == nil {
t.Errorf("SplitHostPort(%q) should have failed", tt.HostPort)
} else {
e := err.(*AddrError)
if e.Err != tt.Err {
t.Errorf("SplitHostPort(%q) = _, _, %q; want %q", tt.HostPort, e.Err, tt.Err)
}
}
}
}
このテストコードは、SplitHostPort
関数が特定の不正な入力に対して、期待される正確なエラーメッセージ(AddrError.Err
フィールド)を返すことを保証します。特に{"[::1]", "missing port in address"}
の追加は、IPv6アドレスのポート欠落に対するエラーメッセージの修正を直接検証しています。
src/pkg/net/ipsock.go
の変更
func splitHostPort(hostport string) (host, port, zone string, err error) {
j, k := 0, 0 // 新しく追加された変数
// The port starts after the last colon.
i := last(hostport, ':')
if i < 0 {
goto missingPort // ポートがない場合はmissingPortラベルへジャンプ
}
if hostport[0] == '[' {
// Expect the first ']' just before the last ':'.
end := byteIndex(hostport, ']')
if end < 0 {
err = &AddrError{"missing ']' in address", hostport}
return
}
switch end + 1 {
case len(hostport):
// There can't be a ':' behind the ']' now.
goto missingPort
case i:
// The expected result.
default:
// Either ']' isn't followed by a colon, or it is
// followed by a colon that is not the last one.
if hostport[end+1] == ':' {
goto tooManyColons
}
goto missingPort
}
host = hostport[1:end]
j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
} else {
host = hostport[:i]
if byteIndex(host, ':') >= 0 {
goto tooManyColons // ホスト部分にコロンが多すぎる場合はtooManyColonsラベルへジャンプ
}
}
if byteIndex(hostport[j:], '[') >= 0 {
err = &AddrError{"unexpected '[' in address", hostport}
return
}
if byteIndex(hostport[k:], ']') >= 0 {
err = &AddrError{"unexpected ']' in address", hostport}
return
}
port = hostport[i+1:]
return
missingPort: // missing port in addressエラーを返す共通の処理
err = &AddrError{"missing port in address", hostport}
return
tooManyColons: // too many colons in addressエラーを返す共通の処理
err = &AddrError{"too many colons in address", hostport}
return
}
この修正の主要な部分は、IPv6アドレスの角括弧の処理をより厳密にした点です。
hostport[0] == '['
のチェックにより、IPv6アドレスの形式を早期に識別します。end := byteIndex(hostport, ']')
で閉じ角括弧の位置を探し、その位置が最後のコロンの直前にあるかどうかをswitch end + 1
ブロックで詳細に検証します。- この検証により、
[::1]
のようにポートがないIPv6アドレスが、missingPort
ラベルに正しく誘導されるようになりました。 - また、
j
とk
というインデックスを導入することで、既に処理した部分を除外して、文字列の残りの部分に予期せぬ角括弧がないかをチェックしています。 goto
ステートメントの使用は、エラー処理ロジックを簡潔にし、コードの重複を避けるためのGo言語における一般的なパターンの一つです。これにより、missing port in address
とtoo many colons in address
という2つの主要なエラーメッセージが、それぞれ対応するラベルにジャンプすることで一貫して生成されます。
関連リンク
- Go言語
net
パッケージのドキュメント: https://pkg.go.dev/net - Go言語
SplitHostPort
関数のドキュメント: https://pkg.go.dev/net#SplitHostPort - GitHub Issue #4526: https://github.com/golang/go/issues/4526
参考にした情報源リンク
- https://golang.org/cl/7038045 (Go Gerrit Change List for this commit)
- https://github.com/golang/go/commit/ad341843a7661e4883c8a66b12433e55db1dfc7e (GitHub Commit Page)
- IPv6アドレスの表記に関する一般的な情報 (RFC 4291など)
- Go言語のエラーハンドリングに関する一般的な情報
- Go言語のテストに関する一般的な情報
net
パッケージのソースコード (src/pkg/net/ipsock.go
,src/pkg/net/ip_test.go
)