golang区分IPv4和IPv6

eoxn13cs  于 2023-08-01  发布在  Go
关注(0)|答案(6)|浏览(136)

对于我正在编写的一个程序,我必须检查IP(将我连接到Internet的IP)是公共的还是私有的。因此,我需要区分IP是IPv4还是IPv6。
我想通过IP的长度来检查它:

conn, err := net.Dial("udp", "8.9.10.11:2342")
if err != nil {
    fmt.Println("Error", err)
}

localaddr := conn.LocalAddr()

addr, _ := net.ResolveUDPAddr("udp", localaddr.String())

ip := addr.IP

fmt.Println(ip)
fmt.Println(len(ip))

字符串
我的IP地址是192.168.2.100,所以是IPv4,但是len(ip)告诉我长度是16,也就是IPv6。我错在哪里?是否存在任何其他方法来区分IPv4和IPv6,并且始终有效?

swvgeqrz

swvgeqrz1#

jimt的答案是正确的,但相当复杂。我只检查ip.To4() != nil。由于文档中说“如果ip不是IPv4地址,To4返回nil”,当且仅当该地址是IPv4地址时,该条件应该返回true

abithluo

abithluo2#

accepted answerEvanip.To4() != nil)解决了这个问题。然而,有一些评论和其他答案检查表示是IPv4还是IPv6,并且它们并不总是准确的:

  • 注**:下面分别列出了IPv4和IPv6的有效符号。每个条目都代表第一个基本条目的变体。这些变化可以根据需要进行组合,但出于空间原因,除了消除歧义之外,没有列出任何变化组合。

有效的IPv4符号:

  • "192.168.0.1":基本
  • "192.168.0.1:80":带端口信息

有效的IPv6符号:

  • "::FFFF:C0A8:1":基本
  • "::FFFF:C0A8:0001":前导零
  • "0000:0000:0000:0000:0000:FFFF:C0A8:1":双冒号展开
  • "::FFFF:C0A8:1%1":带区域信息
  • "::FFFF:192.168.0.1":IPv4文字
  • "[::FFFF:C0A8:1]:80":带端口信息
  • "[::FFFF:C0A8:1%1]:80":带区域和端口信息

所有这些情况(IPv4和IPv6列表)都将被net包视为IPv4地址。IPv6列表的IPv4文字变体将被govalidator视为IPv4。
检查它是IPv4还是IPv6符号的最简单方法如下:

import strings

func IsIPv4(address string) bool {
    return strings.Count(address, ":") < 2
}

func IsIPv6(address string) bool {
    return strings.Count(address, ":") >= 2
}

字符串

axzmvihb

axzmvihb3#

IP的长度几乎总是16,因为net.IP的内部表示在两种情况下都是相同的。来自文档:
请注意,在本文档中,将IP地址称为IPv4地址或IPv6地址是地址的语义属性,而不仅仅是字节片的长度:一个16字节的分片仍然可以是IPv4地址。
区分这两种类型取决于IP的初始化方式。查看net.IPv4()的代码,可以看到它被初始化为16个字节,其中前12个字节被设置为v4InV6Prefix的值。

// IPv4 returns the IP address (in 16-byte form) of the
// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
    p := make(IP, IPv6len)
    copy(p, v4InV6Prefix)
    p[12] = a
    p[13] = b
    p[14] = c
    p[15] = d
    return p
}

字符串
其中v4InV6Prefix定义为:

var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}


如果你想要一个可靠的区分,看看net.IP.To4的源代码是如何处理的:

// To4 converts the IPv4 address ip to a 4-byte representation.
// If ip is not an IPv4 address, To4 returns nil.
func (ip IP) To4() IP {
    if len(ip) == IPv4len {
            return ip
    }
    if len(ip) == IPv6len &&
            isZeros(ip[0:10]) &&
            ip[10] == 0xff &&
            ip[11] == 0xff {
            return ip[12:16]
    }
    return nil
}


isZeros未导出,因此必须在本地复制该代码。然后,您可以简单地执行与上面相同的操作,以确定您使用的是IPv4还是IPv6。

e5nszbig

e5nszbig4#

govalidator检查字符串中是否存在:

func IsIPv6(str string) bool {
  ip := net.ParseIP(str)
  return ip != nil && strings.Contains(str, ":")
}

字符串

m1m5dgzv

m1m5dgzv5#

我会使用net包中的.To4()、.To16()来查找它是IPv4还是IPv6地址。检查blog post。查看博客的最后一部分,例如可能对您有帮助。

5q4ezhmt

5q4ezhmt6#

IPAddress Go library可以通过几行多态代码来实现这一点,可以同时使用IPv4和IPv6地址。Repository here .免责声明:我是项目经理。
它可以处理Adirio回答中列出的所有格式,以及其他格式。
IPv4有“私有”的概念,IPv6有“唯一本地”的概念,两者都有“链路本地”。

type AddrDetails struct {
    isLinkLocal, // both IPv4/6 have the concept of link-local
    isAnyLocal bool // the zero address for either IPv4 or IPv6
}

type IPv4AddrDetails struct {
    isIPv4Private bool
}

type IPv6AddrDetails struct {
    isIPv6UniqueLocal bool
}

func checkAddrStr(addrStr string) (
    address *ipaddr.IPAddress,
    version ipaddr.IPVersion,
    details AddrDetails,
    ipv4Details IPv4AddrDetails, ipv6Details IPv6AddrDetails,
) {
    if host := ipaddr.NewHostName(addrStr); host.IsAddress() {
        address = host.GetAddress()
        version = address.GetIPVersion()
        details = AddrDetails{address.IsLinkLocal(), address.IsAnyLocal()}
        if address.IsIPv4() {
            ipv4Details.isIPv4Private = address.ToIPv4().IsPrivate()
        } else {
            ipv6Details.isIPv6UniqueLocal = address.ToIPv6().IsUniqueLocal()
        }
    }
    return
}

字符串
用你的例子“8.9.10.11:2342”,Adirio的列表和其他一些例子来试试:

addrStrs := []string{
    "8.9.10.11:2342",
    "192.168.0.1", "192.168.0.1:80",
    "::FFFF:C0A8:1", "::FFFF:C0A8:0001",
    "0000:0000:0000:0000:0000:FFFF:C0A8:1",
    "::FFFF:C0A8:1%1", "::FFFF:192.168.0.1",
    "[::FFFF:C0A8:1]:80", "[::FFFF:C0A8:1%1]:80",
    "::", "0.0.0.0",
    "169.254.0.1",
    "fe80::1", "fc00::1",
    "foo",
}
for _, addrStr := range addrStrs {
    addr, version, details, ipv4Details, ipv6Details :=
        checkAddrStr(addrStr)
    if addr == nil {
        fmt.Printf("%s\nnot an address\n", addrStr)
    } else {
        var extra interface{} = ipv4Details
        if addr.IsIPv6() {
            extra = ipv6Details
        }
        unwrap := func(details interface{}) string {
            str := fmt.Sprintf("%+v", details)
            return str[1 : len(str)-1]
        }
        fmt.Printf("%s\n%v %v %+v %+v\n\n", 
            addrStr, addr, version, unwrap(details), unwrap(extra))
    }
}


输出量:

8.9.10.11:2342
8.9.10.11 IPv4 isLinkLocal:false isAnyLocal:false isIPv4Private:false

192.168.0.1
192.168.0.1 IPv4 isLinkLocal:false isAnyLocal:false isIPv4Private:true

192.168.0.1:80
192.168.0.1 IPv4 isLinkLocal:false isAnyLocal:false isIPv4Private:true

::FFFF:C0A8:1
::ffff:c0a8:1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

::FFFF:C0A8:0001
::ffff:c0a8:1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

0000:0000:0000:0000:0000:FFFF:C0A8:1
::ffff:c0a8:1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

::FFFF:C0A8:1%1
::ffff:c0a8:1%1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

::FFFF:192.168.0.1
::ffff:c0a8:1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

[::FFFF:C0A8:1]:80
::ffff:c0a8:1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

[::FFFF:C0A8:1%1]:80
::ffff:c0a8:1%1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:false

::
:: IPv6 isLinkLocal:false isAnyLocal:true isIPv6UniqueLocal:false

0.0.0.0
0.0.0.0 IPv4 isLinkLocal:false isAnyLocal:true isIPv4Private:false

169.254.0.1
169.254.0.1 IPv4 isLinkLocal:true isAnyLocal:false isIPv4Private:false

fe80::1
fe80::1 IPv6 isLinkLocal:true isAnyLocal:false isIPv6UniqueLocal:false

fc00::1
fc00::1 IPv6 isLinkLocal:false isAnyLocal:false isIPv6UniqueLocal:true

foo
not an address

相关问题