2014-03-30 355 views
19

对于我正在开发的程序,我必须检查IP(连接到Internet的IP)是公共还是私人IP。为此,我需要区分IP是IPv4还是IPv6。golang区分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?

回答

27

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

+0

显然,这并不总是准确:https://github.com/asaskevich/govalidator/pull/100 – tmm1

+0

添加@ tmm1的评论,该链接中提出的解决方案也不总是准确的。 ':: FFFF:127.0.0.1'是一个有效的IPv6表示,当它返回False时返回'Is'作为'IsIPv4()'。 – Adirio

+0

最初的问题不是关于*表示*是以v4还是v6的形式出现,而是*地址*是否是有效的v4或v6地址。 ':: FFFF:'是v4InV6Prefix,所以':: FFFF:127.0.0.1'实际上是一个有效的IPv4地址,用于所有的意图和目的。 – Evan

10

IP的长度几乎总是16,因为无论哪种情况,net.IP的内部表示都是相同的。从the documentation

注意,本文档中,指的是一个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。

2

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

2

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

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

govalidator只需要主机,如果有一个端口附加此方法将打破给予误报。 'IsIPv4(str string)bool'方法也会因为“”:: FFFF:192.168.0.1“'而导致误报。 – Adirio

2

accepted answer通过Evanip.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列表)都将被视为IPv4地址the net package。 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 
}