理由
「IPアドレス:ポート番号」の文字列はいろいろな箇所で意味を持った使われ方をするので、よく利用する記法ではあるが、
Goで書かれたネットワークプログラムをIPv6に対応させるためには少し考慮する必要がある。その場合のコード例をメモしておく
参考コード
main.go
package main
import (
"fmt"
"net"
)
func main() {
// 対象のアドレスをスライスに入れる
addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"}
// スライスを1つづつチェック
for i, v := range addrs {
// 例として:80を付加していく
ip := net.ParseIP(v)
fmt.Printf("%d: %s = %s\n", i, v, net.JoinHostPort(ip.String(), "80"))
}
}
- 結果
0: 2001:db8::1 = [2001:db8::1]:80
1: 192.168.0.1 = 192.168.0.1:80
2: 2001:db8:0:0:1:0:0:1 = [2001:db8::1:0:0:1]:80
3: ::1 = [::1]:80
4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = [2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa]:80
5: 2001:db8::aaaa:0:0:1 = [2001:db8::aaaa:0:0:1]:80
6: ::192.0.2.1 = [::c000:201]:80
解説
net.JoinHostPortで結合をするだけ
なぜこのような処理にしなければいけないかは、アンチパターンを見てみましょう
アンチパターン
やってしまいがちなアンチパターンはそのまま文字列処理してしまうこと。
"too many colons in address" と言われちゃいますよ
main.go
package main
import (
"fmt"
)
func main() {
// 対象のアドレスをスライスに入れる
addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"}
// スライスを1つづつチェック
for i, v := range addrs {
// 例として:80を付加していく
fmt.Printf("%d: %s = %s:%d\n", i, v, v,80)
}
}
結果は以下の通りとなります
0: 2001:db8::1 = 2001:db8::1:80
1: 192.168.0.1 = 192.168.0.1:80
2: 2001:db8:0:0:1:0:0:1 = 2001:db8:0:0:1:0:0:1:80
3: ::1 = ::1:80
4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa:80
5: 2001:db8::aaaa:0:0:1 = 2001:db8::aaaa:0:0:1:80
6: ::192.0.2.1 = ::192.0.2.1:80
IPv6アドレスの場合のアドレスリテラル表記で処理しないことが問題となります
まとめ
IPv6アドレスをそのまま文字列結合するのはNG。