0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【GoネットワークプログラムでIPv6対応させる】ログ等のアドレス文字列からアドレスファミリー判別をする方法

Last updated at Posted at 2020-08-06

理由

Goで書かれたネットワークプログラムをIPv6に対応させるためには、サーバサイドはあまり意識しなくてもいいが、アドレス処理を文字列で処理する事がある場合、そのアドレス文字列(ログやサーバの環境変数で見えるクライアントアドレス)がどちらのアドレスファミリーなのかチェックをし、それぞれの処理をする必要がある
その場合のコード例をメモしておく

参考コード

main.go
package main

import (
	"fmt"
	"net"
)

// アドレスファミリーチェック関数
func afCheck(addr string) string {
	// 文字列をnet.IPにParse
	ip := net.ParseIP(addr)

	// Parseできたら
	if ip != nil {
		if len(ip.To4()) == net.IPv4len {
			// アドレスの長さをチェックし、4byte=32bit -> IPv4
			return "ipv4"ß
		} else if len(ip.To16()) == net.IPv6len {
			// アドレスの長さをチェックし、16byte=128bit -> IPv6
			return "ipv6"
		} else {
			// 未知の場合
			return "unknown"
		}
	} else {
		// Parseできない場合
		return "invalid"
	}
}

// メイン関数
func main() {
	// チェック対象のアドレスをスライスに入れる
	addrs := []string{"2001:db8::1", "192.168.0.1", "ffff::", "::1", "12345", "192.168.1", "192.168.1.256","::192.0.2.1"}

	// スライスを1つづつチェック
	for i, v := range addrs {
		// afCheckの返答を確認
		fmt.Printf("%d: %s = %s\n", i, v, afCheck(v))
	}
}

解説

ip.To4(),ip.To16()がどのような動きをするか詳細を見てみると

	// チェック対象のアドレスをスライスに入れる
	addrs := []string{"2001:db8::1", "192.168.0.1", "ffff::", "::1", "12345", "192.168.1", "192.168.1.256", "::192.0.2.1"}

	// スライスを1つづつチェック
	for i, v := range addrs {
		ip := net.ParseIP(v)
		fmt.Printf("%d: %s %#v(%d) %#v(%d)\n", i, v, ip.To4(), len(ip.To4()), ip.To16(), len(ip.To16()))
	}

上記結果を整形すると

i v ip.To4() len(ip.To4()) ip.To16() len(ip.To16()
0 2001:db8::1 net.IP(nil) 0 net.IP{0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1} 16
1 192.168.0.1 net.IP{0xc0, 0xa8, 0x0, 0x1} 4 net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xc0, 0xa8, 0x0, 0x1} 16
2 ffff:: net.IP(nil) 0 net.IP{0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} 16
3 ::1 net.IP(nil) 0 net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1} 16
4 12345 net.IP(nil) 0 net.IP(nil) 0
5 192.168.1 net.IP(nil) 0 net.IP(nil) 0
6 192.168.1.256 net.IP(nil) 0 net.IP(nil) 0
7 ::192.0.2.1 net.IP(nil) 0 net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x0, 0x2, 0x1} 16

これにより、有効なIPアドレスの場合、Parseが成功し

  • IPv4の場合「ip.To4()」に値が入る。(この場合、「ip.To16()」にも値がある。これはIPv4射影アドレス(IPv4-mapped IPv6 address)となっている点注意が必要)
  • IPv6の場合「ip.To4()」に値が入らず、「ip.To16()」に値が入る。

アンチパターン

やってしまいがちなアンチパターンは文字列判断してしまうこと。ここでは単純な判断にしているが、「:」があって「.」がないとか、「:」の数を数えたり、正規表現判断することも同じアンチパターンと考える。

main.go
package main

import (
	"fmt"
	"strings"
)

// アドレスファミリーチェック関数
func afCheck(addr string) string {
	if strings.Contains(addr, ".") {
		// アドレスの文字列から「.」を含んでいたらIPv4と判断
		return "ipv4"
	} else if strings.Contains(addr, ":") {
		// アドレスの文字列から「:」を含んでいたらIPv6と判断
		return "ipv6"
	} else {
		// 未知の場合
		return "unknown"
	}
}

// メイン関数
func main() {
	// チェック対象のアドレスをスライスに入れる
	addrs := []string{"2001:db8::1", "192.168.0.1", "ffff::", "::1", "12345", "192.168.1", "192.168.1.256", "::192.0.2.1"}

	// スライスを1つづつチェック
	for i, v := range addrs {
		// afCheckの返答を確認
		fmt.Printf("%d: %s = %s\n", i, v, afCheck(v))
	}
}

まとめ

アドレスファミリーを文字列で判断するのはNG

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?