diff --git a/common/build.gradle.kts b/common/build.gradle.kts index cf60f46..bd820b0 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -12,13 +12,13 @@ repositories { } dependencies { - implementation("org.yaml:snakeyaml:$snakeYamlVersion") - implementation("io.netty:netty-codec:$nettyVersion") - implementation("io.netty:netty-codec-haproxy:$nettyVersion") - implementation("commons-validator:commons-validator:1.7") - implementation("org.bstats:bstats-base:$bstatsBaseVersion") - implementation("org.slf4j:slf4j-api:$slf4jVersion") - implementation(kotlin("stdlib-jdk8")) + compileOnly("org.yaml:snakeyaml:$snakeYamlVersion") + compileOnly("io.netty:netty-codec:$nettyVersion") + compileOnly("io.netty:netty-codec-haproxy:$nettyVersion") + compileOnly("commons-validator:commons-validator:1.7") + compileOnly("org.bstats:bstats-base:$bstatsBaseVersion") + compileOnly("org.slf4j:slf4j-api:$slf4jVersion") + compileOnly(kotlin("stdlib-jdk8")) } kotlin { diff --git a/common/src/main/java/top/zient/haproxyreduce/common/CIDR.kt b/common/src/main/java/top/zient/haproxyreduce/common/CIDR.kt index 04d63c6..94f508d 100644 --- a/common/src/main/java/top/zient/haproxyreduce/common/CIDR.kt +++ b/common/src/main/java/top/zient/haproxyreduce/common/CIDR.kt @@ -1,6 +1,5 @@ package top.zient.haproxyreduce.common -import org.apache.commons.validator.routines.InetAddressValidator import java.math.BigInteger import java.net.InetAddress import java.net.UnknownHostException diff --git a/common/src/main/java/top/zient/haproxyreduce/common/InetAddressValidator.java b/common/src/main/java/top/zient/haproxyreduce/common/InetAddressValidator.java new file mode 100644 index 0000000..f1bc253 --- /dev/null +++ b/common/src/main/java/top/zient/haproxyreduce/common/InetAddressValidator.java @@ -0,0 +1,206 @@ +/* + * Taken from apache commons 2.1. + */ + +package top.zient.haproxyreduce.common; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *

InetAddress validation and conversion routines (java.net.InetAddress).

+ * + *

This class provides methods to validate a candidate IP address. + * + *

+ * This class is a Singleton; you can retrieve the instance via the {@link #getInstance()} method. + *

+ * + * @version $Revision$ + * @since Validator 1.4 + */ +public class InetAddressValidator implements Serializable { + + private static final int IPV4_MAX_OCTET_VALUE = 255; + + private static final int MAX_UNSIGNED_SHORT = 0xffff; + + private static final int BASE_16 = 16; + + private static final long serialVersionUID = -919201640201914789L; + + private static final String IPV4_REGEX = + "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"; + + // Max number of hex groups (separated by :) in an IPV6 address + private static final int IPV6_MAX_HEX_GROUPS = 8; + + // Max hex digits in each IPv6 group + private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4; + + /** + * Singleton instance of this class. + */ + private static final InetAddressValidator VALIDATOR = new InetAddressValidator(); + + /** IPv4 RegexValidator */ + private final RegexValidator ipv4Validator = new RegexValidator(IPV4_REGEX); + + /** + * Returns the singleton instance of this validator. + * @return the singleton instance of this validator + */ + public static InetAddressValidator getInstance() { + return VALIDATOR; + } + + /** + * Checks if the specified string is a valid IP address. + * @param inetAddress the string to validate + * @return true if the string validates as an IP address + */ + public boolean isValid(String inetAddress) { + return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress); + } + + /** + * Validates an IPv4 address. Returns true if valid. + * @param inet4Address the IPv4 address to validate + * @return true if the argument contains a valid IPv4 address + */ + public boolean isValidInet4Address(String inet4Address) { + // verify that address conforms to generic IPv4 format + String[] groups = ipv4Validator.match(inet4Address); + + if (groups == null) { + return false; + } + + // verify that address subgroups are legal + for (String ipSegment : groups) { + if (ipSegment == null || ipSegment.length() == 0) { + return false; + } + + int iIpSegment = 0; + + try { + iIpSegment = Integer.parseInt(ipSegment); + } catch(NumberFormatException e) { + return false; + } + + if (iIpSegment > IPV4_MAX_OCTET_VALUE) { + return false; + } + + if (ipSegment.length() > 1 && ipSegment.startsWith("0")) { + return false; + } + + } + + return true; + } + + /** + * Validates an IPv6 address. Returns true if valid. + * @param inet6Address the IPv6 address to validate + * @return true if the argument contains a valid IPv6 address + * + * @since 1.4.1 + */ + public boolean isValidInet6Address(String inet6Address) { + String[] parts; + // remove prefix size. This will appear after the zone id (if any) + parts = inet6Address.split("/", -1); + if (parts.length > 2) { + return false; // can only have one prefix specifier + } + if (parts.length == 2) { + if (parts[1].matches("\\d{1,3}")) { // Need to eliminate signs + int bits = Integer.parseInt(parts[1]); // cannot fail because of RE check + if (bits < 0 || bits > 128) { + return false; // out of range + } + } else { + return false; // not a valid number + } + } + // remove zone-id + parts = parts[0].split("%", -1); + if (parts.length > 2) { + return false; + } else if (parts.length == 2){ + // The id syntax is implemenatation independent, but it presumably cannot allow: + // whitespace, '/' or '%' + if (!parts[1].matches("[^\\s/%]+")) { + return false; // invalid id + } + } + inet6Address = parts[0]; + boolean containsCompressedZeroes = inet6Address.contains("::"); + if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) { + return false; + } + if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::")) + || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) { + return false; + } + String[] octets = inet6Address.split(":"); + if (containsCompressedZeroes) { + List octetList = new ArrayList(Arrays.asList(octets)); + if (inet6Address.endsWith("::")) { + // String.split() drops ending empty segments + octetList.add(""); + } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) { + octetList.remove(0); + } + octets = octetList.toArray(new String[octetList.size()]); + } + if (octets.length > IPV6_MAX_HEX_GROUPS) { + return false; + } + int validOctets = 0; + int emptyOctets = 0; // consecutive empty chunks + for (int index = 0; index < octets.length; index++) { + String octet = octets[index]; + if (octet.length() == 0) { + emptyOctets++; + if (emptyOctets > 1) { + return false; + } + } else { + emptyOctets = 0; + // Is last chunk an IPv4 address? + if (index == octets.length - 1 && octet.contains(".")) { + if (!isValidInet4Address(octet)) { + return false; + } + validOctets += 2; + continue; + } + if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) { + return false; + } + int octetInt = 0; + try { + octetInt = Integer.parseInt(octet, BASE_16); + } catch (NumberFormatException e) { + return false; + } + if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) { + return false; + } + } + validOctets++; + } + if (validOctets > IPV6_MAX_HEX_GROUPS || (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes)) { + return false; + } + return true; + } +} + diff --git a/common/src/main/java/top/zient/haproxyreduce/common/RegexValidator.java b/common/src/main/java/top/zient/haproxyreduce/common/RegexValidator.java new file mode 100644 index 0000000..af40908 --- /dev/null +++ b/common/src/main/java/top/zient/haproxyreduce/common/RegexValidator.java @@ -0,0 +1,220 @@ +/* + * Taken from apache commons 2.1. + */ + +package top.zient.haproxyreduce.common; + + +import java.io.Serializable; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * Regular Expression validation (using JDK 1.4+ regex support). + *

+ * Construct the validator either for a single regular expression or a set (array) of + * regular expressions. By default validation is case sensitive but constructors + * are provided to allow case in-sensitive validation. For example to create + * a validator which does case in-sensitive validation for a set of regular + * expressions: + *

+ *
+ * 
+ * String[] regexs = new String[] {...};
+ * RegexValidator validator = new RegexValidator(regexs, false);
+ * 
+ * 
+ * + * + * + * Note that patterns are matched against the entire input. + * + *

+ * Cached instances pre-compile and re-use {@link Pattern}(s) - which according + * to the {@link Pattern} API are safe to use in a multi-threaded environment. + *

+ * + * @version $Revision$ + * @since Validator 1.4 + */ +public class RegexValidator implements Serializable { + + private static final long serialVersionUID = -8832409930574867162L; + + private final Pattern[] patterns; + + /** + * Construct a case sensitive validator for a single + * regular expression. + * + * @param regex The regular expression this validator will + * validate against + */ + public RegexValidator(String regex) { + this(regex, true); + } + + /** + * Construct a validator for a single regular expression + * with the specified case sensitivity. + * + * @param regex The regular expression this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String regex, boolean caseSensitive) { + this(new String[] {regex}, caseSensitive); + } + + /** + * Construct a case sensitive validator that matches any one + * of the set of regular expressions. + * + * @param regexs The set of regular expressions this validator will + * validate against + */ + public RegexValidator(String[] regexs) { + this(regexs, true); + } + + /** + * Construct a validator that matches any one of the set of regular + * expressions with the specified case sensitivity. + * + * @param regexs The set of regular expressions this validator will + * validate against + * @param caseSensitive when true matching is case + * sensitive, otherwise matching is case in-sensitive + */ + public RegexValidator(String[] regexs, boolean caseSensitive) { + if (regexs == null || regexs.length == 0) { + throw new IllegalArgumentException("Regular expressions are missing"); + } + patterns = new Pattern[regexs.length]; + int flags = (caseSensitive ? 0: Pattern.CASE_INSENSITIVE); + for (int i = 0; i < regexs.length; i++) { + if (regexs[i] == null || regexs[i].length() == 0) { + throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); + } + patterns[i] = Pattern.compile(regexs[i], flags); + } + } + + /** + * Validate a value against the set of regular expressions. + * + * @param value The value to validate. + * @return true if the value is valid + * otherwise false. + */ + public boolean isValid(String value) { + if (value == null) { + return false; + } + for (int i = 0; i < patterns.length; i++) { + if (patterns[i].matcher(value).matches()) { + return true; + } + } + return false; + } + + /** + * Validate a value against the set of regular expressions + * returning the array of matched groups. + * + * @param value The value to validate. + * @return String array of the groups matched if + * valid or null if invalid + */ + public String[] match(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + String[] groups = new String[count]; + for (int j = 0; j < count; j++) { + groups[j] = matcher.group(j+1); + } + return groups; + } + } + return null; + } + + + /** + * Validate a value against the set of regular expressions + * returning a String value of the aggregated groups. + * + * @param value The value to validate. + * @return Aggregated String value comprised of the + * groups matched if valid or null if invalid + */ + public String validate(String value) { + if (value == null) { + return null; + } + for (int i = 0; i < patterns.length; i++) { + Matcher matcher = patterns[i].matcher(value); + if (matcher.matches()) { + int count = matcher.groupCount(); + if (count == 1) { + return matcher.group(1); + } + StringBuilder buffer = new StringBuilder(); + for (int j = 0; j < count; j++) { + String component = matcher.group(j+1); + if (component != null) { + buffer.append(component); + } + } + return buffer.toString(); + } + } + return null; + } + + /** + * Provide a String representation of this validator. + * @return A String representation of this validator + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append("RegexValidator{"); + for (int i = 0; i < patterns.length; i++) { + if (i > 0) { + buffer.append(","); + } + buffer.append(patterns[i].pattern()); + } + buffer.append("}"); + return buffer.toString(); + } + +} + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fdd479..5dd3c01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index 84818c8..9d8a9d4 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -1,6 +1,6 @@ plugins { kotlin("jvm") - id("com.gradleup.shadow") version "8.3.5" + id("com.gradleup.shadow") version "9.6.0" } val nettyVersion: String = findProperty("nettyVersion") as String? ?: "4.1.79.Final" @@ -18,8 +18,8 @@ dependencies { compileOnly("org.spigotmc:spigot-api:$spigotApiVersion") compileOnly("io.papermc.paper:paper-api:$paperApiVersion") compileOnly("net.dmulloy2:ProtocolLib:$protocolLibVersion") - implementation("io.netty:netty-codec:$nettyVersion") - implementation("io.netty:netty-codec-haproxy:$nettyVersion") + compileOnly("io.netty:netty-codec:$nettyVersion") + compileOnly("io.netty:netty-codec-haproxy:$nettyVersion") implementation("org.bstats:bstats-bukkit:$bstatsBukkitVersion") implementation(kotlin("stdlib-jdk8")) } diff --git a/paper/src/main/resources/paper-plugin.yml b/paper/src/main/resources/paper-plugin.yml index 51bf658..1bcb221 100644 --- a/paper/src/main/resources/paper-plugin.yml +++ b/paper/src/main/resources/paper-plugin.yml @@ -26,3 +26,10 @@ permissions: haproxyreduce.status: description: 允许查看 HAProxyReduce 状态 default: op + +dependencies: + server: + Sonar: + load: BEFORE + required: false + join-classpath: false diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 40a9d18..2ba9d6d 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -1,6 +1,6 @@ plugins { kotlin("jvm") - id("com.gradleup.shadow") version "8.3.5" + id("com.gradleup.shadow") version "9.6.0" } val nettyVersion: String = findProperty("nettyVersion") as String? ?: "4.1.79.Final" @@ -18,8 +18,8 @@ repositories { dependencies { implementation(project(":common")) compileOnly("com.velocitypowered:velocity-api:$velocityApiVersion") - implementation("io.netty:netty-codec:$nettyVersion") - implementation("io.netty:netty-codec-haproxy:$nettyVersion") + compileOnly("io.netty:netty-codec:$nettyVersion") + compileOnly("io.netty:netty-codec-haproxy:$nettyVersion") implementation("org.yaml:snakeyaml:$snakeYamlVersion") implementation("org.bstats:bstats-velocity:$bstatsVelocityVersion") implementation(kotlin("stdlib")) @@ -32,23 +32,14 @@ kotlin { } } -tasks.register("processPluginJson") { - group = "build" - description = "处理 velocity-plugin.json 中的版本变量" - - from("src/main/resources") { - include("velocity-plugin.json") - filter { line -> - line.replace("\${project.version}", rootProject.version.toString()) - .replace("\${project.name}", project.name.toString()) - } - } - into(layout.buildDirectory.dir("generated/resources/main")) -} - tasks.processResources { - dependsOn("processPluginJson") - exclude("velocity-plugin.json") + duplicatesStrategy = DuplicatesStrategy.INCLUDE + filesMatching("velocity-plugin.json") { + filter { line -> + line.replace("\${project.version}", rootProject.version.toString()) + .replace("\${project.name}", project.name.toString()) + } + } } sourceSets.main { diff --git a/velocity/src/main/resources/velocity-plugin.json b/velocity/src/main/resources/velocity-plugin.json index 2c082dd..add9e83 100644 --- a/velocity/src/main/resources/velocity-plugin.json +++ b/velocity/src/main/resources/velocity-plugin.json @@ -4,5 +4,11 @@ "version": "${project.version}", "main": "top.zient.haproxyreduce.velocity.VelocityMain", "description": "同时支持代理和直连连接", - "authors": ["Wuchang325"] + "authors": ["Wuchang325"], + "dependencies": [ + { + "id": "sonar", + "optional": true + } + ] }