2014-03-30 102 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 
}