Skip to content

Commit 172d694

Browse files
cvictorychickenlj
authored andcommitted
Merge pull request apache#3578, fixes apache#3289, enhance tagRoute: support ip expression match.
1 parent 319a766 commit 172d694

File tree

7 files changed

+448
-6
lines changed

7 files changed

+448
-6
lines changed

LICENSE

+5
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,8 @@ This product contains a modified portion of 'Netty', an event-driven asynchronou
220220
* io.netty.util.Timeout
221221
* io.netty.util.HashedWheelTimer
222222

223+
For the org.apache.dubbo.common.utils.CIDRUtils :
224+
225+
This product contains a modified portion of 'edazdarevic.commons.net.CIDRUtils',
226+
under a "MIT License" license, see https://github.com/edazdarevic/CIDRUtils/blob/master/CIDRUtils.java
227+

dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/condition/config/ListenableRouter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private synchronized void init(String ruleKey) {
120120
String routerKey = ruleKey + RULE_SUFFIX;
121121
configuration.addListener(routerKey, this);
122122
String rule = configuration.getConfig(routerKey);
123-
if (rule != null) {
123+
if (StringUtils.isNotEmpty(rule)) {
124124
this.process(new ConfigChangeEvent(routerKey, rule));
125125
}
126126
}

dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/router/tag/TagRouter.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.dubbo.common.logger.Logger;
2222
import org.apache.dubbo.common.logger.LoggerFactory;
2323
import org.apache.dubbo.common.utils.CollectionUtils;
24+
import org.apache.dubbo.common.utils.NetUtils;
2425
import org.apache.dubbo.common.utils.StringUtils;
2526
import org.apache.dubbo.configcenter.ConfigChangeEvent;
2627
import org.apache.dubbo.configcenter.ConfigChangeType;
@@ -33,6 +34,7 @@
3334
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRouterRule;
3435
import org.apache.dubbo.rpc.cluster.router.tag.model.TagRuleParser;
3536

37+
import java.net.UnknownHostException;
3638
import java.util.List;
3739
import java.util.function.Predicate;
3840
import java.util.stream.Collectors;
@@ -196,11 +198,26 @@ private <T> List<Invoker<T>> filterInvoker(List<Invoker<T>> invokers, Predicate<
196198
}
197199

198200
private boolean addressMatches(URL url, List<String> addresses) {
199-
return addresses != null && addresses.contains(url.getAddress());
201+
return addresses != null && checkAddressMatch(addresses, url.getHost(), url.getPort());
200202
}
201203

202204
private boolean addressNotMatches(URL url, List<String> addresses) {
203-
return addresses == null || !addresses.contains(url.getAddress());
205+
return addresses == null || !checkAddressMatch(addresses, url.getHost(), url.getPort());
206+
}
207+
208+
private boolean checkAddressMatch(List<String> addresses, String host, int port) {
209+
for (String address : addresses) {
210+
try {
211+
if (NetUtils.matchIpExpression(address, host, port)) {
212+
return true;
213+
}
214+
} catch (UnknownHostException e) {
215+
logger.error("The format of ip address is invalid in tag route. Address :" + address, e);
216+
} catch (Exception e) {
217+
logger.error("The format of ip address is invalid in tag route. Address :" + address, e);
218+
}
219+
}
220+
return false;
204221
}
205222

206223
public void setApplication(String app) {
@@ -232,7 +249,7 @@ public <T> void notify(List<Invoker<T>> invokers) {
232249
configuration.addListener(key, this);
233250
application = providerApplication;
234251
String rawRule = configuration.getConfig(key);
235-
if (rawRule != null) {
252+
if (StringUtils.isNotEmpty(rawRule)) {
236253
this.process(new ConfigChangeEvent(key, rawRule));
237254
}
238255
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2013 Edin Dazdarevic ([email protected])
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE.
20+
*
21+
**/
22+
package org.apache.dubbo.common.utils;
23+
24+
import java.math.BigInteger;
25+
import java.net.InetAddress;
26+
import java.net.UnknownHostException;
27+
import java.nio.ByteBuffer;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
31+
/**
32+
* A class that enables to get an IP range from CIDR specification. It supports
33+
* both IPv4 and IPv6.
34+
* <p>
35+
* From https://github.com/edazdarevic/CIDRUtils/blob/master/CIDRUtils.java
36+
*/
37+
public class CIDRUtils {
38+
private final String cidr;
39+
40+
private InetAddress inetAddress;
41+
private InetAddress startAddress;
42+
private InetAddress endAddress;
43+
private final int prefixLength;
44+
45+
46+
public CIDRUtils(String cidr) throws UnknownHostException {
47+
48+
this.cidr = cidr;
49+
50+
/* split CIDR to address and prefix part */
51+
if (this.cidr.contains("/")) {
52+
int index = this.cidr.indexOf("/");
53+
String addressPart = this.cidr.substring(0, index);
54+
String networkPart = this.cidr.substring(index + 1);
55+
56+
inetAddress = InetAddress.getByName(addressPart);
57+
prefixLength = Integer.parseInt(networkPart);
58+
59+
calculate();
60+
} else {
61+
throw new IllegalArgumentException("not an valid CIDR format!");
62+
}
63+
}
64+
65+
66+
private void calculate() throws UnknownHostException {
67+
68+
ByteBuffer maskBuffer;
69+
int targetSize;
70+
if (inetAddress.getAddress().length == 4) {
71+
maskBuffer =
72+
ByteBuffer
73+
.allocate(4)
74+
.putInt(-1);
75+
targetSize = 4;
76+
} else {
77+
maskBuffer = ByteBuffer.allocate(16)
78+
.putLong(-1L)
79+
.putLong(-1L);
80+
targetSize = 16;
81+
}
82+
83+
BigInteger mask = (new BigInteger(1, maskBuffer.array())).not().shiftRight(prefixLength);
84+
85+
ByteBuffer buffer = ByteBuffer.wrap(inetAddress.getAddress());
86+
BigInteger ipVal = new BigInteger(1, buffer.array());
87+
88+
BigInteger startIp = ipVal.and(mask);
89+
BigInteger endIp = startIp.add(mask.not());
90+
91+
byte[] startIpArr = toBytes(startIp.toByteArray(), targetSize);
92+
byte[] endIpArr = toBytes(endIp.toByteArray(), targetSize);
93+
94+
this.startAddress = InetAddress.getByAddress(startIpArr);
95+
this.endAddress = InetAddress.getByAddress(endIpArr);
96+
97+
}
98+
99+
private byte[] toBytes(byte[] array, int targetSize) {
100+
int counter = 0;
101+
List<Byte> newArr = new ArrayList<Byte>();
102+
while (counter < targetSize && (array.length - 1 - counter >= 0)) {
103+
newArr.add(0, array[array.length - 1 - counter]);
104+
counter++;
105+
}
106+
107+
int size = newArr.size();
108+
for (int i = 0; i < (targetSize - size); i++) {
109+
110+
newArr.add(0, (byte) 0);
111+
}
112+
113+
byte[] ret = new byte[newArr.size()];
114+
for (int i = 0; i < newArr.size(); i++) {
115+
ret[i] = newArr.get(i);
116+
}
117+
return ret;
118+
}
119+
120+
public String getNetworkAddress() {
121+
122+
return this.startAddress.getHostAddress();
123+
}
124+
125+
public String getBroadcastAddress() {
126+
return this.endAddress.getHostAddress();
127+
}
128+
129+
public boolean isInRange(String ipAddress) throws UnknownHostException {
130+
InetAddress address = InetAddress.getByName(ipAddress);
131+
BigInteger start = new BigInteger(1, this.startAddress.getAddress());
132+
BigInteger end = new BigInteger(1, this.endAddress.getAddress());
133+
BigInteger target = new BigInteger(1, address.getAddress());
134+
135+
int st = start.compareTo(target);
136+
int te = target.compareTo(end);
137+
138+
return (st == -1 || st == 0) && (te == -1 || te == 0);
139+
}
140+
}

dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java

+135-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class NetUtils {
5757
private static final Map<String, String> hostNameCache = new LRUCache<>(1000);
5858
private static volatile InetAddress LOCAL_ADDRESS = null;
5959

60+
private static final String SPLIT_IPV4_CHARECTER = "\\.";
61+
private static final String SPLIT_IPV6_CHARECTER = ":";
62+
6063
public static int getRandomPort() {
6164
return RND_PORT_START + ThreadLocalRandom.current().nextInt(RND_PORT_RANGE);
6265
}
@@ -340,13 +343,13 @@ public static String toURL(String protocol, String host, int port, String path)
340343
return sb.toString();
341344
}
342345

343-
public static void joinMulticastGroup (MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException {
346+
public static void joinMulticastGroup(MulticastSocket multicastSocket, InetAddress multicastAddress) throws IOException {
344347
setInterface(multicastSocket, multicastAddress instanceof Inet6Address);
345348
multicastSocket.setLoopbackMode(false);
346349
multicastSocket.joinGroup(multicastAddress);
347350
}
348351

349-
public static void setInterface (MulticastSocket multicastSocket, boolean preferIpv6) throws IOException{
352+
public static void setInterface(MulticastSocket multicastSocket, boolean preferIpv6) throws IOException {
350353
boolean interfaceSet = false;
351354
Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
352355
while (interfaces.hasMoreElements()) {
@@ -370,4 +373,134 @@ public static void setInterface (MulticastSocket multicastSocket, boolean prefer
370373
}
371374
}
372375

376+
public static boolean matchIpExpression(String pattern, String host, int port) throws UnknownHostException {
377+
378+
// if the pattern is subnet format, it will not be allowed to config port param in pattern.
379+
if (pattern.contains("/")) {
380+
CIDRUtils utils = new CIDRUtils(pattern);
381+
return utils.isInRange(host);
382+
}
383+
384+
385+
return matchIpRange(pattern, host, port);
386+
}
387+
388+
/**
389+
* @param pattern
390+
* @param host
391+
* @param port
392+
* @return
393+
* @throws UnknownHostException
394+
*/
395+
public static boolean matchIpRange(String pattern, String host, int port) throws UnknownHostException {
396+
if (pattern == null || host == null) {
397+
throw new IllegalArgumentException("Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host);
398+
}
399+
pattern = pattern.trim();
400+
if (pattern.equals("*.*.*.*") || pattern.equals("*")) {
401+
return true;
402+
}
403+
404+
InetAddress inetAddress = InetAddress.getByName(host);
405+
boolean isIpv4 = isValidV4Address(inetAddress) ? true : false;
406+
String[] hostAndPort = getPatternHostAndPort(pattern, isIpv4);
407+
if (hostAndPort[1] != null && !hostAndPort[1].equals(String.valueOf(port))) {
408+
return false;
409+
}
410+
pattern = hostAndPort[0];
411+
412+
String splitCharacter = SPLIT_IPV4_CHARECTER;
413+
if (!isIpv4) {
414+
splitCharacter = SPLIT_IPV6_CHARECTER;
415+
}
416+
String[] mask = pattern.split(splitCharacter);
417+
//check format of pattern
418+
checkHostPattern(pattern, mask, isIpv4);
419+
420+
host = inetAddress.getHostAddress();
421+
422+
String[] ip_address = host.split(splitCharacter);
423+
if (pattern.equals(host)) {
424+
return true;
425+
}
426+
// short name condition
427+
if (!ipPatternContainExpression(pattern)) {
428+
InetAddress patternAddress = InetAddress.getByName(pattern);
429+
if (patternAddress.getHostAddress().equals(host)) {
430+
return true;
431+
} else {
432+
return false;
433+
}
434+
}
435+
for (int i = 0; i < mask.length; i++) {
436+
if (mask[i].equals("*") || mask[i].equals(ip_address[i])) {
437+
continue;
438+
} else if (mask[i].contains("-")) {
439+
String[] rangeNumStrs = mask[i].split("-");
440+
if (rangeNumStrs.length != 2) {
441+
throw new IllegalArgumentException("There is wrong format of ip Address: " + mask[i]);
442+
}
443+
Integer min = getNumOfIpSegment(rangeNumStrs[0], isIpv4);
444+
Integer max = getNumOfIpSegment(rangeNumStrs[1], isIpv4);
445+
Integer ip = getNumOfIpSegment(ip_address[i], isIpv4);
446+
if (ip < min || ip > max) {
447+
return false;
448+
}
449+
} else if ("0".equals(ip_address[i]) && ("0".equals(mask[i]) || "00".equals(mask[i]) || "000".equals(mask[i]) || "0000".equals(mask[i]))) {
450+
continue;
451+
} else if (!mask[i].equals(ip_address[i])) {
452+
return false;
453+
}
454+
}
455+
return true;
456+
}
457+
458+
private static boolean ipPatternContainExpression(String pattern) {
459+
return pattern.contains("*") || pattern.contains("-");
460+
}
461+
462+
private static void checkHostPattern(String pattern, String[] mask, boolean isIpv4) {
463+
if (!isIpv4) {
464+
if (mask.length != 8 && ipPatternContainExpression(pattern)) {
465+
throw new IllegalArgumentException("If you config ip expression that contains '*' or '-', please fill qulified ip pattern like 234e:0:4567:0:0:0:3d:*. ");
466+
}
467+
if (mask.length != 8 && !pattern.contains("::")) {
468+
throw new IllegalArgumentException("The host is ipv6, but the pattern is not ipv6 pattern : " + pattern);
469+
}
470+
} else {
471+
if (mask.length != 4) {
472+
throw new IllegalArgumentException("The host is ipv4, but the pattern is not ipv4 pattern : " + pattern);
473+
}
474+
}
475+
}
476+
477+
private static String[] getPatternHostAndPort(String pattern, boolean isIpv4) {
478+
String[] result = new String[2];
479+
if (pattern.startsWith("[") && pattern.contains("]:")) {
480+
int end = pattern.indexOf("]:");
481+
result[0] = pattern.substring(1, end);
482+
result[1] = pattern.substring(end + 2);
483+
return result;
484+
} else if (pattern.startsWith("[") && pattern.endsWith("]")) {
485+
result[0] = pattern.substring(1, pattern.length() - 1);
486+
result[1] = null;
487+
return result;
488+
} else if (isIpv4 && pattern.contains(":")) {
489+
int end = pattern.indexOf(":");
490+
result[0] = pattern.substring(0, end);
491+
result[1] = pattern.substring(end + 1);
492+
return result;
493+
} else {
494+
result[0] = pattern;
495+
return result;
496+
}
497+
}
498+
499+
private static Integer getNumOfIpSegment(String ipSegment, boolean isIpv4) {
500+
if (isIpv4) {
501+
return Integer.parseInt(ipSegment);
502+
}
503+
return Integer.parseInt(ipSegment, 16);
504+
}
505+
373506
}

0 commit comments

Comments
 (0)