feature: support ipv6

Dubbo

访问 PR 界面:https://github.com/apache/incubator-seata/pull/5902

本地查看:

1
2
3
4
5
$ git remote -vv                                                       
origin git@github.com:solisamicus/incubator-seata.git (fetch)
origin git@github.com:solisamicus/incubator-seata.git (push)
upstream https://github.com/apache/incubator-seata.git (fetch)
upstream https://github.com/apache/incubator-seata.git (push)

找到合并提交:

1
2
3
4
5
6
$ git log upstream/2.x --grep="feature: support ipv6"
commit df32ebe431c49b22889dae1816c621c3994914ce
Author: Ifdevil <384867771@qq.com>
Date: Mon Oct 9 13:47:42 2023 +0800

feature: support ipv6 (#5902)

查看具体改动:

1
$ git show df32ebe431c49b22889dae1816c621c3994914ce

工具类

Java

$ \text {NetAddressValidatorUtil}$:链接

常量成员:

1
2
3
4
5
6
7
private static final String PERCENT = "%"
private static final String DOUBLE_COLON = "::"
private static final String DOUBLE_COLON_FFFF = "::ffff:"
private static final String FE80 = "fe80:"
private static final int ZERO = 0
private static final int SEVEN = 7
private static final int FIVE = 5

正则表达式成员:

1
2
3
4
5
private static final Pattern IPV4_PATTERN
private static final Pattern IPV6_STD_PATTERN
private static final Pattern IPV6_HEX_COMPRESSED_PATTERN
private static final Pattern IPV6_MIXED_COMPRESSED_REGEX
private static final Pattern IPV6_MIXED_UNCOMPRESSED_REGEX

公共方法:

1
2
3
4
5
6
7
public static boolean isIPv4Address(String input)
public static boolean isIPv6Address(String input)
public static boolean isIPv6StdAddress(String input)
public static boolean isIPv6HexCompressedAddress(String input)
public static boolean isIPv6MixedAddress(String input)
public static boolean isIPv6IPv4MappedAddress(String input)
public static boolean isLinkLocalIPv6WithZoneIndex(String input)

$\text {NetUtil}$:链接

常量成员:

1
2
3
4
5
6
7
8
private static final Logger LOGGER
public static final boolean PREFER_IPV6_ADDRESSES
private static final String LOCALHOST = "127.0.0.1"
private static final String ANY_HOST = "0.0.0.0"
public static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1"
public static final String LOCALHOST_SHORT_IPV6 = "::1"
public static final String ANY_HOST_IPV6 = "0:0:0:0:0:0:0:0"
public static final String ANY_HOST_SHORT_IPV6 = "::"

缓存成员:

1
2
private static volatile InetAddress LOCAL_ADDRESS = null
private static final Set<String> FORBIDDEN_HOSTS

地址转换方法:

1
2
3
4
5
6
public static String toStringAddress(SocketAddress address)
public static String toIpAddress(SocketAddress address)
public static String toStringAddress(InetSocketAddress address)
public static InetSocketAddress toInetSocketAddress(String address)
public static String[] splitIPPortStr(String address)
public static long toLong(String address)

本地地址方法:

1
2
3
4
5
public static String getLocalIp(String... preferredNetworks)
public static String localIP()
public static String getLocalHost()
public static InetAddress getLocalAddress(String... preferredNetworks)
private static InetAddress getLocalAddress0(String... preferredNetworks)

地址验证方法:

1
2
3
4
5
6
7
8
public static void validAddress(InetSocketAddress address)
private static boolean isValidAddress(InetAddress address)
public static boolean isValidIp(String ip, boolean validLocalAndAny)
private static String convertIpIfNecessary(String ip)
public static boolean isValidIPv4(String ip)
public static boolean isValidIPv6(String ip)
private static boolean isUniqueLocalAddress(InetAddress address)
private static String removeBrackets(String str)

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package util

import (
"regexp"
"strings"
)

type IPValidator struct {
percent string
doubleColon string
doubleColonFFFF string
fe80 string

ipv4Pattern *regexp.Regexp
ipv6StdPattern *regexp.Regexp
ipv6HexCompressedPattern *regexp.Regexp
ipv6MixedCompressedPattern *regexp.Regexp
ipv6MixedUncompressedPattern *regexp.Regexp
}

var defaultIPValidator = newIPValidator()

func newIPValidator() *IPValidator {
validator := &IPValidator{
percent: "%",
doubleColon: "::",
doubleColonFFFF: "::ffff:",
fe80: "fe80:",
}

validator.ipv4Pattern = regexp.MustCompile(`^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$`)
validator.ipv6StdPattern = regexp.MustCompile(`^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$`)
validator.ipv6HexCompressedPattern = regexp.MustCompile(`^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$`)
validator.ipv6MixedCompressedPattern = regexp.MustCompile(`^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:)*)?)$`)
validator.ipv6MixedUncompressedPattern = regexp.MustCompile(`^(?:[0-9a-fA-F]{1,4}:){6}$`)

return validator
}

func IsIPv4(input string) bool {
return defaultIPValidator.isIPv4(input)
}

func (v *IPValidator) isIPv4(input string) bool {
return v.ipv4Pattern.MatchString(input)
}

func IsIPv6(input string) bool {
return defaultIPValidator.isIPv6(input)
}

func (v *IPValidator) isIPv6(input string) bool {
return v.isIPv6Std(input) ||
v.isIPv6HexCompressed(input) ||
v.isLinkLocalIPv6WithZoneIndex(input) ||
v.isIPv6IPv4Mapped(input) ||
v.isIPv6Mixed(input)
}

func IsIPv6Std(input string) bool {
return defaultIPValidator.isIPv6Std(input)
}

func (v *IPValidator) isIPv6Std(input string) bool {
return v.ipv6StdPattern.MatchString(input)
}

func IsIPv6HexCompressed(input string) bool {
return defaultIPValidator.isIPv6HexCompressed(input)
}

func (v *IPValidator) isIPv6HexCompressed(input string) bool {
return v.ipv6HexCompressedPattern.MatchString(input)
}

func IsIPv6Mixed(input string) bool {
return defaultIPValidator.isIPv6Mixed(input)
}

func (v *IPValidator) isIPv6Mixed(input string) bool {
splitIndex := strings.LastIndex(input, ":")
if splitIndex == -1 {
return false
}

ipv4Part := input[splitIndex+1:]
ipv4Valid := v.isIPv4(ipv4Part)

ipv6Part := input[:splitIndex+1]
if ipv6Part == v.doubleColon {
return ipv4Valid
}

ipv6Uncompressed := v.ipv6MixedUncompressedPattern.MatchString(ipv6Part)
ipv6Compressed := v.ipv6MixedCompressedPattern.MatchString(ipv6Part)

return ipv4Valid && (ipv6Uncompressed || ipv6Compressed)
}

func IsIPv6IPv4Mapped(input string) bool {
return defaultIPValidator.isIPv6IPv4Mapped(input)
}

func (v *IPValidator) isIPv6IPv4Mapped(input string) bool {
if len(input) > 7 && strings.EqualFold(input[:7], v.doubleColonFFFF) {
return v.isIPv4(input[7:])
}
return false
}

func IsLinkLocalIPv6WithZoneIndex(input string) bool {
return defaultIPValidator.isLinkLocalIPv6WithZoneIndex(input)
}

func (v *IPValidator) isLinkLocalIPv6WithZoneIndex(input string) bool {
if len(input) > 5 && strings.EqualFold(input[:5], v.fe80) {
lastIndex := strings.LastIndex(input, v.percent)
if lastIndex > 0 && lastIndex < len(input)-1 {
ipPart := input[:lastIndex]
return v.isIPv6Std(ipPart) || v.isIPv6HexCompressed(ipPart)
}
}
return false
}

未完待续…