LoginSignup
0
0

More than 3 years have passed since last update.

【GoネットワークプログラムでIPv6対応させる】アドレス正規化をする方法

Last updated at Posted at 2020-08-06

理由

Goで書かれたネットワークプログラムをIPv6に対応させるためには、サーバサイドはあまり意識しなくてもいいが、IPv6アドレスは表記方法が複数あり、このIPv6アドレス処理を文字列で処理しようとする場合正規化する必要がある。その場合のコード例をメモしておく

例)
- 2001:db8::1 は 2001:0db8:0000:0000:0000:0000:0000:0001 ともかける→文字列比較すると違うと判断されてしまう
参考) RFC5952-IPv6アドレスの推奨表記

参考コード

main.go
package main

import (
    "fmt"
    "net"
)

func v6Format(addr string) string {
    ip := net.ParseIP(addr)
    // Parseできたら
    if ip != nil {
        return ip.String()
    } else {
        return "error"
    }
}

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.168.1.256", "::192.0.2.1"}

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

解説

v6Formatがどのような動きをするか詳細を見てみると、文字列をParseIPして、Stringで文字列化しているだけ。
これだけで正規化できます

  • input
2001:db8:0:0:1:0:0:1
2001:0db8:0:0:1:0:0:1
2001:db8::1:0:0:1
2001:db8::0:1:0:0:1
2001:0db8::1:0:0:1
2001:db8:0:0:1::1
2001:db8:0000:0:1::1
2001:DB8:0:0:1::1
2001:db8:aaaa:bbbb:cccc:dddd::1
2001:db8:aaaa:bbbb:cccc:dddd:0:1
2001:db8:0:0:0::1
2001:db8:0:0::1
2001:db8:0::1
2001:db8::1
2001:db8::aaaa:0:0:1
2001:db8:0:0:aaaa::1
2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa
2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA
2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa
  • output
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8::1:0:0:1
2001:db8:aaaa:bbbb:cccc:dddd:0:1
2001:db8:aaaa:bbbb:cccc:dddd:0:1
2001:db8::1
2001:db8::1
2001:db8::1
2001:db8::1
2001:db8::aaaa:0:0:1
2001:db8::aaaa:0:0:1
2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa
2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa
2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa

アンチパターン

やってしまいがちなアンチパターンは文字列処理してしまうこと。正規表現判断することも同じアンチパターンと考える。

main.go
package main

import (
    "fmt"
    "strings"
)

func v6Format(addr string) string {
    addr = strings.Replace(addr, ":0000", ":", -1)
    addr = strings.Replace(addr, ":::", ":", -1)
    addr = strings.Replace(addr, ":0", ":", -1)
    return strings.ToLower(addr)
}

func main() {
    // チェック対象のアドレスをスライスに入れる
    addrs := []string{"2001:0db8:0000:0000:0000:0000:0000:0001", "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.168.1.256", "::192.0.2.1"}

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

FULL形式に全部してしまえ

package main

import (
    "fmt"
    "net"
)

func v6Format(addr string) string {
    ip := net.ParseIP(addr)
    full := ""
    for i, j := range ip {
        if i > 0 && i%2 == 0 {
            full = full + ":"
        }
        full = full + fmt.Sprintf("%02x", j)
    }
    return full
}

func main() {
    // チェック対象のアドレスをスライスに入れる
    addrs := []string{"2001:0db8:0000:0000:0000:0000:0000:0001", "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.168.1.256", "::192.0.2.1"}

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

まとめ

IPv6アドレスを正規化せずに判断するのはNG。正規化を文字列変換するのも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