Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1f7368716
|
||
|
|
1d990d20a2
|
||
|
|
3b53ee919c | ||
|
|
88ea5bd3e2 | ||
|
|
da25b6a7b6 | ||
|
|
a1a412f45a | ||
|
|
014e0ea62e | ||
|
|
79c30df06a | ||
|
|
456d64fa69 | ||
|
|
554d6c300e | ||
|
|
133cef037e | ||
|
|
bb7511d37f | ||
|
|
78666de764 | ||
|
|
0534b8eae8 | ||
|
|
1af7f1efde | ||
|
|
41ff3f412c | ||
|
|
cfbd6ffac5 | ||
|
|
cf97e37632 | ||
|
|
1629334e71 | ||
|
|
8cb66fc87a | ||
|
|
e868bab503 | ||
|
|
2daad65b33 | ||
|
|
846f4d0a6e | ||
|
|
c07b71c5b1 | ||
|
|
4b13ac2ca1 | ||
|
|
860fba8d6e | ||
|
|
99cca8aa33 | ||
|
|
ad2a29de0f | ||
|
|
17a28b1c57 | ||
|
|
eb8663f5b8 | ||
|
|
be2fe38744 | ||
|
|
e740a28f77 | ||
|
|
d8a755035c | ||
|
|
188d541c4d | ||
|
|
40138413bb | ||
|
|
2475bdfabd | ||
|
|
71ba01d8e7 | ||
|
|
8416dbe4e3 | ||
|
|
863fd644cf | ||
|
|
f91a5c54b6 | ||
|
|
44715187c4 | ||
|
|
75e30ec6b3 | ||
|
|
f0f1c53ba8 | ||
|
|
5779306940 | ||
|
|
d031708308 | ||
|
|
46f7e9385b | ||
|
|
f100c0578a | ||
|
|
15c0543114 | ||
|
|
a1d6664906 | ||
|
|
22b0b7065d | ||
|
|
e66f4df4ee | ||
|
|
74bc2b069c | ||
|
|
637c64916b | ||
|
|
0a8d6295b2 | ||
|
|
1d7f2c368a | ||
|
|
086bdd99d5 | ||
|
|
d01aa5efc4 | ||
|
|
d67ece7a0d | ||
|
|
615e4b2289 | ||
|
|
ddf722c069 | ||
|
|
1b716d5e18 | ||
|
|
839d625ce9 | ||
|
|
8a01e915b1 | ||
|
|
32d422a952 | ||
|
|
c5cd43f2fa | ||
|
|
d69d4f7ba8 | ||
|
|
52325a8a2c | ||
|
|
b07fe80979 | ||
|
|
c6471d8e15 | ||
|
|
0dd3c25421 | ||
|
|
42530caa57 | ||
|
|
e58319cabe | ||
|
|
1de480d575 | ||
|
|
1959106ac8 | ||
|
|
5750391e43 | ||
|
|
00f50110fe | ||
|
|
e14d937a70 | ||
|
|
3d5dab69bb | ||
|
|
a4302e4de8 | ||
|
|
e859a1c95a | ||
|
|
a1e9b84b47 | ||
|
|
af998585e9 | ||
|
|
62b18b0842 | ||
|
|
8c0d98fda4 | ||
|
|
78d22e627c | ||
|
|
a6dc0865f7 | ||
|
|
7907d8f731 | ||
|
|
32142f9961 | ||
|
|
f8d32f5e96 | ||
|
|
f8da84de07 | ||
|
|
cafc278c32 | ||
|
|
e958718bcb | ||
|
|
031c2ed091 | ||
|
|
faf7568c81 | ||
|
|
812f5c353f | ||
|
|
6216b0c01e | ||
|
|
901ce0cfeb | ||
|
|
a537099297 | ||
|
|
7a14a45338 | ||
|
|
8f35cf9532 |
No files matched your search
@@ -0,0 +1 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
+17
-3
@@ -1,4 +1,18 @@
|
||||
# Testing
|
||||
ip-whitelist/
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
*/target/
|
||||
*/*/target/
|
||||
*.iml
|
||||
*.iml
|
||||
|
||||
# Eclipse
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
|
||||
# Gradle
|
||||
.gradle/
|
||||
|
||||
# Build
|
||||
libs/
|
||||
build/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 TCPShield
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -6,12 +6,12 @@ It also parses passed IP addresses so the server is aware of the real player IP
|
||||
|
||||
### Compatibility
|
||||
|
||||
TCPShield is compatible with Paper, Spigot / CraftBukkit, BungeeCord and Velocity.
|
||||
TCPShield is compatible with Spigot / CraftBukkit, BungeeCord and Velocity.
|
||||
|
||||
When using Spigot / CraftBukkit, [ProtocolLib](https://github.com/aadnk/ProtocolLib) needs to be installed. This is not necessary when Paper is being used.
|
||||
When using Spigot / CraftBukkit, [ProtocolLib](https://github.com/aadnk/ProtocolLib) needs to be installed.
|
||||
|
||||
### Setup
|
||||
Setting up the plugin is easy as cake. Please follow [these](https://docs.tcpshield.com/onboarding-1/tcpshield-plugin) guidelines.
|
||||
Setting up the plugin is easy as pie. Please follow [these](https://docs.tcpshield.com/panel/tcpshield-plugin) guidelines.
|
||||
|
||||
### Compiling
|
||||
In order to compile TCPShield, [install Gradle](https://docs.gradle.org/current/userguide/installation.html) and run the following command in the project folder:
|
||||
@@ -22,11 +22,13 @@ gradle build
|
||||
The dependencies should install themselves automatically. After the build has finished, the compiled jar file can be found under `/build/libs`.
|
||||
|
||||
### Support
|
||||
See [Contact](https://docs.tcpshield.com/about-us)
|
||||
See [Contact](https://tcpshield.com/#contact)
|
||||
|
||||
### Contributors
|
||||
|
||||
These wonderful contributors have helped TCPShield make this plugin better!
|
||||
|
||||
* [Fuzzlemann](https://github.com/Fuzzlemann)
|
||||
* [Dylan Keir](https://github.com/DylanKeir)
|
||||
* [Paul Zhang](https://github.com/paulzhng)
|
||||
* [RyanDeLap](https://github.com/RyanDeLap)
|
||||
* [PlumpOrange](https://github.com/xPlumpOrange/)
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import org.yaml.snakeyaml.DumperOptions
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath group: 'org.yaml', name: 'snakeyaml', version: '1.26'
|
||||
classpath group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
|
||||
}
|
||||
maven {
|
||||
url = 'https://repo.dmulloy2.net/nexus/repository/public/'
|
||||
}
|
||||
maven {
|
||||
url = 'https://oss.sonatype.org/content/repositories/snapshots'
|
||||
}
|
||||
maven {
|
||||
url = 'https://papermc.io/repo/repository/maven-public/'
|
||||
}
|
||||
maven {
|
||||
url = 'https://repo.velocitypowered.com/snapshots/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Bukkit
|
||||
compileOnly group: 'org.spigotmc', name: 'spigot-api', version: '1.11-R0.1-SNAPSHOT'
|
||||
compileOnly group: 'com.comphenix.protocol', name: 'ProtocolLib', version: '4.4.0'
|
||||
|
||||
// Paper
|
||||
compileOnly group: 'com.destroystokyo.paper', name: 'paper-api', version: '1.15.2-R0.1-SNAPSHOT'
|
||||
|
||||
// BungeeCord
|
||||
compileOnly group: 'net.md-5', name: 'bungeecord-api', version: '1.14-SNAPSHOT'
|
||||
|
||||
// Velocity
|
||||
compileOnly group: 'com.velocitypowered', name: 'velocity-api', version: '1.0.0-SNAPSHOT'
|
||||
|
||||
// Testing
|
||||
testImplementation group: 'org.junit.jupiter', name:'junit-jupiter-api', version: '5.7.0-M1'
|
||||
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.7.0-M1'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
configurations {
|
||||
testImplementation.extendsFrom compileOnly
|
||||
}
|
||||
|
||||
version = '2.1'
|
||||
group = 'net.tcpshield.tcpshield'
|
||||
archivesBaseName = 'TCPShield'
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs('src/main/java')
|
||||
}
|
||||
resources {
|
||||
srcDirs('src/main/resources')
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs('src/test/java')
|
||||
}
|
||||
resources {
|
||||
srcDirs('src/test/resources')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = targetCompatibility = '1.8'
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
task updateVersion {
|
||||
updateYaml()
|
||||
updateJson()
|
||||
}
|
||||
|
||||
void updateYaml() {
|
||||
DumperOptions options = new DumperOptions()
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK)
|
||||
options.setSplitLines(false)
|
||||
options.setPrettyFlow(true)
|
||||
|
||||
def yaml = new Yaml(options)
|
||||
|
||||
def bukkitYamlFile = new File('src/main/resources/plugin.yml')
|
||||
def bungeeYamlFile = new File('src/main/resources/bungee.yml')
|
||||
|
||||
def files = [bukkitYamlFile, bungeeYamlFile]
|
||||
files.each { file ->
|
||||
file.newInputStream().withCloseable { inputStream ->
|
||||
def cfg = yaml.load(inputStream)
|
||||
cfg.put("version", version)
|
||||
|
||||
new FileWriter(file).withCloseable { writer ->
|
||||
yaml.dump(cfg, writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateJson() {
|
||||
def velocityJson = new File('src/main/resources/velocity-plugin.json')
|
||||
|
||||
def files = [velocityJson]
|
||||
files.each { file ->
|
||||
file.newInputStream().withCloseable { inputStream ->
|
||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8).withCloseable { inputStreamReader ->
|
||||
JsonObject object = JsonParser.parseReader(inputStreamReader).getAsJsonObject()
|
||||
object.addProperty("version", version.toString())
|
||||
|
||||
new FileWriter(file).withCloseable { writer ->
|
||||
writer.write(object.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
build.dependsOn updateVersion
|
||||
@@ -0,0 +1,138 @@
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import org.yaml.snakeyaml.DumperOptions
|
||||
import org.yaml.snakeyaml.Yaml
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.yaml:snakeyaml:1.26")
|
||||
classpath("com.google.code.gson:gson:2.8.6")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("java")
|
||||
id("eclipse")
|
||||
id("idea")
|
||||
}
|
||||
|
||||
version = "2.8.0"
|
||||
group = "net.tcpshield.tcpshield"
|
||||
|
||||
base {
|
||||
archivesName.set("TCPShield")
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
isDownloadJavadoc = true
|
||||
isDownloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
named("main") {
|
||||
java.srcDir("src/main/java")
|
||||
resources.srcDir("src/main/resources")
|
||||
}
|
||||
named("test") {
|
||||
java.srcDir("src/test/java")
|
||||
resources.srcDir("src/test/resources")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
sourceCompatibility = "17"
|
||||
targetCompatibility = "17"
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven { url = uri("https://repo1.maven.org/maven2/") }
|
||||
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
|
||||
maven { url = uri("https://repo.dmulloy2.net/nexus/repository/public/") }
|
||||
maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") }
|
||||
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
|
||||
maven { url = uri("https://repo.opencollab.dev/maven-snapshots/") }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(files("libs/ProtocolLib-5.0.jar")) // Import Legacy ProtocolLib API.
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0-M1")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0-M1")
|
||||
compileOnly("org.geysermc.floodgate:api:2.1.1-SNAPSHOT")
|
||||
}
|
||||
|
||||
tasks.named<ProcessResources>("processResources") {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
tasks.named<Test>("test") {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
configurations {
|
||||
named("testImplementation") {
|
||||
extendsFrom(configurations.named("compileOnly").get())
|
||||
}
|
||||
}
|
||||
|
||||
fun updateYamls() {
|
||||
val options = DumperOptions().apply {
|
||||
defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
|
||||
setSplitLines(false)
|
||||
setPrettyFlow(true)
|
||||
}
|
||||
val yaml = Yaml(options)
|
||||
val files = listOf(
|
||||
File("src/main/resources/plugin.yml"),
|
||||
File("src/main/resources/bungee.yml")
|
||||
)
|
||||
files.forEach { file ->
|
||||
file.inputStream().use { inputStream ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val cfg = yaml.load<MutableMap<String, Any?>>(inputStream)
|
||||
cfg["version"] = project.version.toString()
|
||||
FileWriter(file).use { writer ->
|
||||
yaml.dump(cfg, writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateJsons() {
|
||||
val file = File("src/main/resources/velocity-plugin.json")
|
||||
file.inputStream().use { inputStream ->
|
||||
InputStreamReader(inputStream, StandardCharsets.UTF_8).use { reader ->
|
||||
val obj: JsonObject = JsonParser.parseReader(reader).asJsonObject
|
||||
obj.addProperty("version", project.version.toString())
|
||||
FileWriter(file).use { writer ->
|
||||
writer.write(obj.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("updateVersion") {
|
||||
doLast {
|
||||
updateYamls()
|
||||
updateJsons()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn("updateVersion")
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+7
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
rootProject.name = "TCPShield"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static long MAX_TIME_DIFFERENCE = 3; // in seconds
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
import net.tcpshield.tcpshield.exception.*;
|
||||
import net.tcpshield.tcpshield.validation.TimestampValidation;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HandshakePacketHandler {
|
||||
|
||||
private final Logger logger;
|
||||
private final SignatureVerifier signatureVerifier;
|
||||
private final TimestampValidation timestampValidation;
|
||||
private final TCPShieldConfig config;
|
||||
|
||||
public HandshakePacketHandler(Logger logger, TCPShieldConfig config) {
|
||||
try {
|
||||
this.logger = logger;
|
||||
this.signatureVerifier = new SignatureVerifier();
|
||||
this.timestampValidation = new TimestampValidation(config);
|
||||
this.config = config;
|
||||
} catch (Exception e) {
|
||||
throw new TCPShieldInitializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onHandshake(IPacket packet, IPlayer player) {
|
||||
String rawPayload = packet.getRawPayload();
|
||||
|
||||
try {
|
||||
String extraData = null;
|
||||
|
||||
// fix for e.g. incoming FML tagged packets
|
||||
String cleanedPayload;
|
||||
int nullIndex = rawPayload.indexOf('\0');
|
||||
if (nullIndex != -1) { // FML tagged
|
||||
cleanedPayload = rawPayload.substring(0, nullIndex);
|
||||
extraData = rawPayload.substring(nullIndex);
|
||||
} else { // standard
|
||||
cleanedPayload = rawPayload;
|
||||
}
|
||||
|
||||
String[] payload = cleanedPayload.split("///", 4);
|
||||
if (payload.length != 4)
|
||||
throw new MalformedPayloadException("payload.length != 4. Raw payload = \"" + rawPayload + "\"");
|
||||
|
||||
String hostname = payload[0];
|
||||
String ipData = payload[1];
|
||||
int timestamp;
|
||||
try {
|
||||
timestamp = Integer.parseInt(payload[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new MalformedPayloadException(e);
|
||||
}
|
||||
String signature = payload[3];
|
||||
|
||||
if (!timestampValidation.checkTimestamp(timestamp))
|
||||
throw new InvalidTimestampException(timestamp, timestampValidation.getTime());
|
||||
|
||||
String[] hostnameParts = ipData.split(":");
|
||||
String host = hostnameParts[0];
|
||||
int port = Integer.parseInt(hostnameParts[1]);
|
||||
|
||||
String reconstructedPayload = hostname + "///" + host + ":" + port + "///" + timestamp;
|
||||
|
||||
if (!signatureVerifier.verify(reconstructedPayload, signature))
|
||||
throw new SigningVerificationFailureException();
|
||||
|
||||
InetSocketAddress newIP = new InetSocketAddress(host, port);
|
||||
player.setIP(newIP);
|
||||
|
||||
if (extraData != null) hostname = hostname + extraData;
|
||||
|
||||
packet.modifyOriginalPacket(hostname);
|
||||
} catch (InvalidTimestampException e) {
|
||||
handleInvalidTimestamp(player, e.getTimestamp(), e.getCurrentTime());
|
||||
} catch (SigningVerificationFailureException e) {
|
||||
handleSigningVerificationFailure(player, rawPayload);
|
||||
} catch (ConnectionNotProxiedException e) {
|
||||
handleNotProxiedConnection(player, rawPayload);
|
||||
} catch (IPModificationFailureException e) {
|
||||
this.logger.warning(String.format("%s[%s/%s]'s IP failed to be modified. Raw payload = \"%s\"", player.getName(), player.getUUID(), player.getIP(), rawPayload));
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleInvalidTimestamp(IPlayer player, long timestamp, long currentTime) {
|
||||
if (config.isDebug()) {
|
||||
this.logger.warning(String.format("%s[%s/%s] provided valid handshake information, but timestamp was not valid. " +
|
||||
"Provided timestamp: %d vs. system timestamp: %d. Please check your machine time. Timestamp validation mode: %s", player.getName(), player.getUUID(), player.getIP(), timestamp, currentTime, config.getTimestampValidationMode()));
|
||||
}
|
||||
|
||||
if (config.isOnlyProxy()) {
|
||||
player.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSigningVerificationFailure(IPlayer player, String rawPayload) {
|
||||
if (config.isDebug()) {
|
||||
this.logger.warning(String.format("%s[%s/%s] provided valid handshake information, but signing check failed. Raw payload = \"%s\"", player.getName(), player.getUUID(), player.getIP(), rawPayload));
|
||||
}
|
||||
|
||||
if (config.isOnlyProxy()) {
|
||||
player.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNotProxiedConnection(IPlayer player, String rawPayload) {
|
||||
if (!config.isOnlyProxy()) return;
|
||||
|
||||
if (config.isDebug()) {
|
||||
this.logger.info(String.format("%s[%s/%s] was disconnected because no proxy info was received and only-allow-proxy-connections is enabled. Raw payload = \"%s\"", player.getName(), player.getUUID(), player.getIP(), rawPayload));
|
||||
}
|
||||
|
||||
player.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
private static final Table<Class<?>, String, Field> CACHED_FIELDS = HashBasedTable.create();
|
||||
|
||||
public static void setFinalField(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
|
||||
setFinalField(object, getPrivateField(object.getClass(), fieldName), value);
|
||||
}
|
||||
|
||||
public static void setFinalField(Object object, Field field, Object value) throws IllegalAccessException, NoSuchFieldException {
|
||||
field.setAccessible(true);
|
||||
if (Modifier.isFinal(field.getModifiers())) {
|
||||
Field modifiersField = getDeclaredField(Field.class, "modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
}
|
||||
|
||||
setField(object, field, value);
|
||||
}
|
||||
|
||||
public static void setField(Object object, String fieldName, Object value) throws IllegalAccessException, NoSuchFieldException {
|
||||
setField(object, getPrivateField(object.getClass(), fieldName), value);
|
||||
}
|
||||
|
||||
public static void setField(Object object, Field field, Object value) throws IllegalAccessException {
|
||||
field.set(object, value);
|
||||
}
|
||||
|
||||
public static Object getObjectInPrivateField(Object object, String fieldName) throws IllegalAccessException, NoSuchFieldException {
|
||||
Field field = getPrivateField(object.getClass(), fieldName);
|
||||
return field.get(object);
|
||||
}
|
||||
|
||||
public static Field getPrivateField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||
Field field = getDeclaredField(clazz, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
|
||||
public static Field searchFieldByClass(Class<?> clazz, Class<?> searchFor) {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getType() == searchFor) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("no " + searchFor.getName() + " field for clazz = " + clazz.getName() + " found");
|
||||
}
|
||||
|
||||
public static Field getDeclaredField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||
Field cachedField = CACHED_FIELDS.get(clazz, fieldName);
|
||||
if (cachedField != null) return cachedField;
|
||||
|
||||
Field field;
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return getDeclaredField(superclass, fieldName);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
CACHED_FIELDS.put(clazz, fieldName, field);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
public class SignatureVerifier {
|
||||
|
||||
private final PublicKey publicKey;
|
||||
|
||||
public SignatureVerifier() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
byte[] encodedKey = ByteStreams.toByteArray(SignatureVerifier.class.getResourceAsStream("/signing_pub.key"));
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
publicKey = keyFactory.generatePublic(keySpec);
|
||||
}
|
||||
|
||||
public boolean verify(String str, String signature) {
|
||||
return verify(str.getBytes(StandardCharsets.UTF_8), signature);
|
||||
}
|
||||
|
||||
private boolean verify(byte[] data, String signature) {
|
||||
try {
|
||||
byte[] decodedSignature = Base64.getDecoder().decode(signature);
|
||||
|
||||
Signature sig = Signature.getInstance("SHA512withECDSA");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data);
|
||||
|
||||
return sig.verify(decodedSignature);
|
||||
} catch (IllegalArgumentException | SignatureException | NoSuchAlgorithmException | InvalidKeyException e) { // catching for illegal base64 encoding or invalid encoding for signatures
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
import net.tcpshield.tcpshield.geyser.GeyserUtils;
|
||||
import net.tcpshield.tcpshield.provider.PacketProvider;
|
||||
import net.tcpshield.tcpshield.provider.PlayerProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.parse.InvalidPayloadException;
|
||||
import net.tcpshield.tcpshield.util.exception.parse.SignatureValidationException;
|
||||
import net.tcpshield.tcpshield.util.exception.parse.TimestampValidationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InvalidSecretException;
|
||||
import net.tcpshield.tcpshield.util.validation.SignatureValidator;
|
||||
import net.tcpshield.tcpshield.util.validation.cidr.CIDRValidator;
|
||||
import net.tcpshield.tcpshield.util.validation.timestamp.TimestampValidator;
|
||||
import net.tcpshield.tcpshield.util.validation.timestamp.impl.HTPDateTimestampValidator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Packet handler for TCPShield's plugins
|
||||
*/
|
||||
public class TCPShieldPacketHandler {
|
||||
|
||||
private final TCPShieldPlugin plugin;
|
||||
|
||||
private TimestampValidator timestampValidator;
|
||||
private SignatureValidator signatureValidator;
|
||||
private CIDRValidator cidrValidator;
|
||||
|
||||
/**
|
||||
* Creates an instance of the TCPShieldPacketHandler
|
||||
*
|
||||
* @param plugin The TCPShield plugin
|
||||
*/
|
||||
public TCPShieldPacketHandler(TCPShieldPlugin plugin) throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
||||
this.plugin = plugin;
|
||||
|
||||
initValidators();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the validators
|
||||
*
|
||||
* @throws NoSuchAlgorithmException SignatureValidator exception
|
||||
* @throws IOException SignatureValidator exception
|
||||
* @throws InvalidKeySpecException SignatureValidator exception
|
||||
*/
|
||||
private void initValidators() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
||||
signatureValidator = new SignatureValidator();
|
||||
|
||||
switch (plugin.getConfigProvider().getTimestampValidationMode().toLowerCase()) {
|
||||
case "system": {
|
||||
timestampValidator = TimestampValidator.createDefault(plugin);
|
||||
break;
|
||||
}
|
||||
case "htpdate": {
|
||||
timestampValidator = new HTPDateTimestampValidator(plugin);
|
||||
break;
|
||||
}
|
||||
case "off": {
|
||||
timestampValidator = TimestampValidator.createEmpty(plugin);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
plugin.getDebugger().warn(
|
||||
"Unknown timestamp validation mode \"%s\"! Disabling timestamp validation.",
|
||||
plugin.getConfigProvider().getTimestampValidationMode());
|
||||
timestampValidator = TimestampValidator.createEmpty(plugin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cidrValidator = new CIDRValidator(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the packet and player manipulation
|
||||
* for the initial handshake from TCPShield
|
||||
* servers
|
||||
*
|
||||
* @param packet The handshake packet
|
||||
* @param player The involved player
|
||||
*/
|
||||
public void handleHandshake(PacketProvider packet, PlayerProvider player) throws HandshakeException {
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getByName(player.getIP());
|
||||
|
||||
String extraData = null;
|
||||
String[] payload = packet.getPayloadString().split("///");
|
||||
|
||||
if (payload.length != 4)
|
||||
if (cidrValidator.validate(inetAddress))
|
||||
return; // Allow connection with no processing
|
||||
else
|
||||
throw new InvalidPayloadException("length: " + payload.length + ", payload: " + Arrays.toString(payload) + ", raw payload: " + packet.getPayloadString());
|
||||
|
||||
int nullIndex;
|
||||
if ((nullIndex = payload[3].indexOf('\0')) != -1) { // FML tagged payload
|
||||
String originalData = payload[3];
|
||||
payload[3] = originalData.substring(0, nullIndex);
|
||||
extraData = originalData.substring(nullIndex);
|
||||
}
|
||||
|
||||
String hostname = payload[0];
|
||||
String ipData = payload[1];
|
||||
int timestamp = Integer.parseInt(payload[2]);
|
||||
String signature = payload[3];
|
||||
|
||||
String[] ipParts;
|
||||
String host;
|
||||
int port;
|
||||
|
||||
if (timestamp == 0 && GeyserUtils.GEYSER_SUPPORT_ENABLED) {
|
||||
// Remap the altered layout
|
||||
ipData = payload[0];
|
||||
signature = payload[1];
|
||||
hostname = payload[3];
|
||||
|
||||
// This is annoying having to have this in both blocks but w/e
|
||||
ipParts = ipData.split(":");
|
||||
host = ipParts[0];
|
||||
port = Integer.parseInt(ipParts[1]);
|
||||
|
||||
if (!signature.equals(GeyserUtils.SESSION_SECRET)) {
|
||||
throw new InvalidSecretException("Invalid secret: " + signature);
|
||||
}
|
||||
} else {
|
||||
ipParts = ipData.split(":");
|
||||
host = ipParts[0];
|
||||
port = Integer.parseInt(ipParts[1]);
|
||||
String reconstructedPayload = hostname + "///" + host + ":" + port + "///" + timestamp;
|
||||
|
||||
if (!timestampValidator.validate(timestamp))
|
||||
throw new TimestampValidationException(timestampValidator, timestamp);
|
||||
|
||||
|
||||
if (!signatureValidator.validate(reconstructedPayload, signature))
|
||||
throw new SignatureValidationException();
|
||||
}
|
||||
|
||||
InetSocketAddress newIP = new InetSocketAddress(host, port);
|
||||
player.setIP(newIP);
|
||||
|
||||
if (extraData != null) hostname = hostname + extraData;
|
||||
|
||||
|
||||
packet.setPacketHostname(hostname);
|
||||
} catch (NumberFormatException | InvalidPayloadException e) {
|
||||
plugin.getDebugger().warn(
|
||||
"%s[%s/%s] was disconnected because no proxy info was received and only-allow-proxy-connections is enabled. Raw payload = \"%s\"",
|
||||
player.getName(), player.getUUID(), player.getIP(), packet.getPayloadString());
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
if (!(e instanceof InvalidPayloadException))
|
||||
throw new HandshakeException(new InvalidPayloadException(e));
|
||||
else
|
||||
throw new HandshakeException(e);
|
||||
} catch (TimestampValidationException e) {
|
||||
plugin.getDebugger().warn(
|
||||
"%s[%s/%s] provided valid handshake information, but timestamp was not valid. " +
|
||||
"Provided timestamp: %d vs. system timestamp: %d. Please check your machine time. Timestamp validation mode: %s",
|
||||
player.getName(), player.getUUID(), player.getIP(), e.getTimestamp(), e.getTimestampValidator().getUnixTime(), plugin.getConfigProvider().getTimestampValidationMode());
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
throw new HandshakeException(e);
|
||||
} catch (SignatureValidationException e) {
|
||||
plugin.getDebugger().warn(
|
||||
"%s[%s/%s] provided valid handshake information, but signing check failed. Raw payload = \"%s\"",
|
||||
player.getName(), player.getUUID(), player.getIP(), packet.getPayloadString());
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
throw new HandshakeException(e);
|
||||
} catch (UnknownHostException e) {
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
throw new HandshakeException(e);
|
||||
} catch (InvalidSecretException e) {
|
||||
plugin.getDebugger().warn(
|
||||
"%s[%s/%s] provided an invalid session secret when authenticating a geyser connection.",
|
||||
player.getName(), player.getUUID(), player.getIP());
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
throw new HandshakeException(e);
|
||||
} catch (Exception e) {
|
||||
if (plugin.getConfigProvider().isOnlyProxy())
|
||||
player.disconnect();
|
||||
|
||||
if (!(e instanceof HandshakeException))
|
||||
throw new HandshakeException(e);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.tcpshield.tcpshield;
|
||||
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
import net.tcpshield.tcpshield.util.Debugger;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The base/provider for all entry points/main classes
|
||||
*/
|
||||
public interface TCPShieldPlugin {
|
||||
|
||||
/**
|
||||
* Gets the plugin's config provider
|
||||
*
|
||||
* @return The plugin's config provider
|
||||
*/
|
||||
ConfigProvider getConfigProvider();
|
||||
|
||||
/**
|
||||
* Gets the plugin's logger
|
||||
*
|
||||
* @return The plugin's logger
|
||||
*/
|
||||
Logger getLogger();
|
||||
|
||||
/**
|
||||
* Gets the packet handler
|
||||
*
|
||||
* @return The packet handler
|
||||
*/
|
||||
TCPShieldPacketHandler getPacketHandler();
|
||||
|
||||
/**
|
||||
* Gets the plugin's debugger
|
||||
*
|
||||
* @return The plugin's debugger
|
||||
*/
|
||||
Debugger getDebugger();
|
||||
|
||||
|
||||
/**
|
||||
* Default initialization of TCPShield, called after interface defaults are set
|
||||
*/
|
||||
default void initialization() {
|
||||
String jvmVersion = System.getProperty("java.version");
|
||||
try {
|
||||
// Java 11 check/warning (Will eventually force Java 16 without warning)
|
||||
String[] versionParts = jvmVersion.split("\\.");
|
||||
int baseVersion = Integer.parseInt(versionParts[0]);
|
||||
if (baseVersion < 11) // Java 8, and below, starts with 1, but since we are using Java 11 we can ignore sub values
|
||||
this.getDebugger().warn("The Java version you are running is outdated for TCPShield and may cause issues. Update to atleast Java 11. Your version: Java %s", jvmVersion);
|
||||
} catch (Throwable t) {
|
||||
this.getDebugger().error("Failed to check java version for string: " + jvmVersion);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package net.tcpshield.tcpshield.abstraction;
|
||||
|
||||
public interface IPacket {
|
||||
|
||||
String getRawPayload();
|
||||
|
||||
void modifyOriginalPacket(String hostname) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package net.tcpshield.tcpshield.abstraction;
|
||||
|
||||
import net.tcpshield.tcpshield.exception.IPModificationFailureException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public interface IPlayer {
|
||||
|
||||
String getUUID();
|
||||
|
||||
String getName();
|
||||
|
||||
String getIP();
|
||||
|
||||
void setIP(InetSocketAddress ip) throws IPModificationFailureException;
|
||||
|
||||
void disconnect();
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package net.tcpshield.tcpshield.abstraction;
|
||||
|
||||
public abstract class TCPShieldConfig {
|
||||
|
||||
protected boolean onlyProxy;
|
||||
protected String timestampValidationMode;
|
||||
protected boolean debug;
|
||||
|
||||
public boolean isOnlyProxy() {
|
||||
return this.onlyProxy;
|
||||
}
|
||||
|
||||
public String getTimestampValidationMode() {
|
||||
return this.timestampValidationMode;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package net.tcpshield.tcpshield.bukkit;
|
||||
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigLoadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigReloadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigResetException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* Bukkit's configuration implementation
|
||||
*/
|
||||
public class BukkitConfig extends ConfigProvider {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
private FileConfiguration loadedConfiguration;
|
||||
|
||||
public BukkitConfig(JavaPlugin plugin) throws ConfigException {
|
||||
this.plugin = plugin;
|
||||
this.dataFolder = plugin.getDataFolder();
|
||||
this.configFile = new File(dataFolder, "config.yml");
|
||||
|
||||
try {
|
||||
reload();
|
||||
} catch (Exception e) {
|
||||
throw new ConfigException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkNodes(String... nodes) {
|
||||
for (String node : nodes) {
|
||||
if (!loadedConfiguration.contains(node))
|
||||
throw new ConfigException("The node \"" + node + "\" does not exist in the config.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() throws ConfigResetException {
|
||||
try {
|
||||
loadedConfiguration = null; // To the garbage collector you go
|
||||
|
||||
try {
|
||||
configFile.delete();
|
||||
} catch (Exception ignored) {
|
||||
// Just ignore since it either does not exist, or we can overwrite
|
||||
}
|
||||
|
||||
try (InputStream in = plugin.getResource("config.yml")) { // Have to use Bukkit's resource streaming or the resource wont be found
|
||||
Files.copy(in, configFile.toPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ConfigResetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() throws ConfigLoadException {
|
||||
try {
|
||||
loadedConfiguration = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
checkNodes("only-allow-proxy-connections", "timestamp-validation", "debug-mode", "enable-geyser-support", "prefer-protocollib");
|
||||
|
||||
this.onlyProxy = loadedConfiguration.getBoolean("only-allow-proxy-connections");
|
||||
this.timestampValidationMode = loadedConfiguration.getString("timestamp-validation");
|
||||
this.doDebug = loadedConfiguration.getBoolean("debug-mode");
|
||||
this.geyser = loadedConfiguration.getBoolean("enable-geyser-support");
|
||||
this.preferProtocolLib = loadedConfiguration.getBoolean("prefer-protocollib");
|
||||
} catch (Exception e) {
|
||||
throw new ConfigLoadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws ConfigReloadException {
|
||||
try {
|
||||
if (!dataFolder.exists())
|
||||
dataFolder.mkdir();
|
||||
|
||||
if (!configFile.exists())
|
||||
reset();
|
||||
|
||||
try {
|
||||
load();
|
||||
} catch (ConfigLoadException exception) {
|
||||
plugin.getLogger().warning("Config loading failed, resetting to default config. (This can be ignored if you just switched builds of TCPShield)");
|
||||
reset();
|
||||
reload(); // Redo cycle, possible StackOverFlow, but realistically only happens if reset fails
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ConfigReloadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +1,100 @@
|
||||
package net.tcpshield.tcpshield.bukkit;
|
||||
|
||||
import net.tcpshield.tcpshield.bukkit.paper.TCPShieldPaper;
|
||||
import net.tcpshield.tcpshield.bukkit.protocollib.TCPShieldProtocolLib;
|
||||
import java.util.logging.Logger;
|
||||
import net.tcpshield.tcpshield.TCPShieldPacketHandler;
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.bukkit.paper.BukkitPaper;
|
||||
import net.tcpshield.tcpshield.bukkit.protocollib.BukkitProtocolLib;
|
||||
import net.tcpshield.tcpshield.bukkit.provider.BukkitImplProvider;
|
||||
import net.tcpshield.tcpshield.geyser.GeyserUtils;
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
import net.tcpshield.tcpshield.util.Debugger;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class TCPShieldBukkit extends JavaPlugin {
|
||||
/**
|
||||
* The entry point for Bukkit servers
|
||||
*/
|
||||
public class TCPShieldBukkit extends JavaPlugin implements TCPShieldPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (isPaper()) {
|
||||
new TCPShieldPaper(this).load();
|
||||
} else {
|
||||
new TCPShieldProtocolLib(this).load();
|
||||
}
|
||||
}
|
||||
private ConfigProvider configProvider;
|
||||
private TCPShieldPacketHandler packetHandler;
|
||||
private Debugger debugger;
|
||||
|
||||
private boolean isPaper() {
|
||||
if (true) return false; // disabled for now
|
||||
private BukkitImplProvider bukkitImpl;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
configProvider = new BukkitConfig(this);
|
||||
debugger = Debugger.createDebugger(this);
|
||||
packetHandler = new TCPShieldPacketHandler(this);
|
||||
|
||||
// check force plib option -> paper -> plib -> error
|
||||
if (this.configProvider.preferProtocolLib() && getServer().getPluginManager().getPlugin("ProtocolLib") != null) {
|
||||
try {
|
||||
String[] protocolLibVersion = getServer().getPluginManager().getPlugin("ProtocolLib").getDescription().getVersion().split("-")[0].split("\\.");
|
||||
int major = Integer.parseInt(protocolLibVersion[0]);
|
||||
int minor = Integer.parseInt(protocolLibVersion[1]);
|
||||
int patch = Integer.parseInt(protocolLibVersion[2]);
|
||||
|
||||
String paperVersion = Bukkit.getServer().getMinecraftVersion();
|
||||
if (major <= 5 && minor <= 2 && patch <= 1 && (paperVersion.equals("1.20.5") || paperVersion.equals("1.20.6"))) {
|
||||
getLogger().severe("TCPShield is incompatible with ProtocolLib <= 5.2.1 on Paper 1.20.5/1.20.6 due to lack of support from ProtocolLib. Reverting to default Paper handler to prevent issues. This error can be avoided by disabling 'prefer-protocollib' in the config.");
|
||||
bukkitImpl = new BukkitPaper(this);
|
||||
} else {
|
||||
bukkitImpl = new BukkitProtocolLib(this);
|
||||
}
|
||||
} catch (Exception t) {
|
||||
getLogger().warning("Failed to check Paper or ProtocolLib version. This is not a critical error unless you are running Paper 1.20.5/1.20.6 with ProtocolLib version 5.2.1 or below.");
|
||||
getDebugger().exception(t);
|
||||
|
||||
bukkitImpl = new BukkitProtocolLib(this);
|
||||
}
|
||||
} else if (BukkitImplProvider.hasPaperEvent()) {
|
||||
bukkitImpl = new BukkitPaper(this);
|
||||
} else if (getServer().getPluginManager().getPlugin("ProtocolLib") != null) {
|
||||
bukkitImpl = new BukkitProtocolLib(this);
|
||||
} else {
|
||||
getLogger().severe("TCPShield not loading because ProtocolLib is not installed. Either use Paper to enable native compatibility or install ProtocolLib.");
|
||||
return;
|
||||
}
|
||||
|
||||
bukkitImpl.load();
|
||||
|
||||
GeyserUtils.initGeyser(this, configProvider);
|
||||
|
||||
initialization();
|
||||
} catch (Exception e) {
|
||||
throw new InitializationException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The provider's base methods
|
||||
*/
|
||||
|
||||
@Override
|
||||
public TCPShieldPacketHandler getPacketHandler() {
|
||||
return packetHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Debugger getDebugger() {
|
||||
return debugger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigProvider getConfigProvider() {
|
||||
return configProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return super.getLogger();
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.event.player.PlayerHandshakeEvent");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.impl;
|
||||
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class BukkitConfigImpl extends TCPShieldConfig {
|
||||
|
||||
public BukkitConfigImpl(JavaPlugin javaPlugin) {
|
||||
javaPlugin.saveDefaultConfig();
|
||||
|
||||
FileConfiguration config = javaPlugin.getConfig();
|
||||
|
||||
this.onlyProxy = config.getBoolean("only-allow-proxy-connections");
|
||||
this.timestampValidationMode = config.getString("timestamp-validation");
|
||||
this.debug = config.getBoolean("debug-mode");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper;
|
||||
|
||||
import net.tcpshield.tcpshield.bukkit.TCPShieldBukkit;
|
||||
import net.tcpshield.tcpshield.bukkit.paper.handler.PaperHandshakeHandler;
|
||||
import net.tcpshield.tcpshield.bukkit.provider.BukkitImplProvider;
|
||||
|
||||
/**
|
||||
* Bukkit provider for the PaperSpigot implementation of TCPShield
|
||||
*/
|
||||
public class BukkitPaper extends BukkitImplProvider {
|
||||
|
||||
public BukkitPaper(TCPShieldBukkit bukkitPlugin) {
|
||||
super(bukkitPlugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
PaperHandshakeHandler packetHandler = new PaperHandshakeHandler(this);
|
||||
getPlugin().getServer().getPluginManager().registerEvents(packetHandler, getPlugin());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.tcpshield.tcpshield.HandshakePacketHandler;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.bukkit.impl.BukkitConfigImpl;
|
||||
import net.tcpshield.tcpshield.bukkit.paper.impl.PaperPacketImpl;
|
||||
import net.tcpshield.tcpshield.bukkit.paper.impl.PaperPlayerImpl;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class PaperHandshakePacketHandler implements Listener {
|
||||
|
||||
private final HandshakePacketHandler handshakePacketHandler;
|
||||
|
||||
public PaperHandshakePacketHandler(JavaPlugin plugin) {
|
||||
this.handshakePacketHandler = new HandshakePacketHandler(plugin.getLogger(), new BukkitConfigImpl(plugin));
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onHandshake(PlayerHandshakeEvent e) {
|
||||
IPacket packet = new PaperPacketImpl(e);
|
||||
IPlayer player = new PaperPlayerImpl(e);
|
||||
|
||||
handshakePacketHandler.onHandshake(packet, player);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onServerPing(PaperServerListPingEvent e) {
|
||||
if (e.getClient().isLegacy()) e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class TCPShieldPaper {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public TCPShieldPaper(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
PaperHandshakePacketHandler packetHandler = new PaperHandshakePacketHandler(plugin);
|
||||
Bukkit.getPluginManager().registerEvents(packetHandler, plugin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper.handler;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.tcpshield.tcpshield.bukkit.provider.BukkitImplProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
/**
|
||||
* The handshake handler for PaperSpigot
|
||||
*/
|
||||
public class PaperHandshakeHandler implements Listener {
|
||||
|
||||
private final BukkitImplProvider bukkitProvider;
|
||||
|
||||
public PaperHandshakeHandler(BukkitImplProvider bukkitProvider) {
|
||||
this.bukkitProvider = bukkitProvider;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onHandshake(PlayerHandshakeEvent e) {
|
||||
PaperPacket packet = new PaperPacket(e);
|
||||
PaperPlayer player = new PaperPlayer(e);
|
||||
|
||||
try {
|
||||
bukkitProvider.getPlugin().getPacketHandler().handleHandshake(packet, player);
|
||||
} catch (HandshakeException exception) {
|
||||
bukkitProvider.getPlugin().getDebugger().exception(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onServerPing(PaperServerListPingEvent e) {
|
||||
if (e.getClient().isLegacy())
|
||||
e.setCancelled(true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper.handler;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import net.tcpshield.tcpshield.provider.PacketProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PacketManipulationException;
|
||||
|
||||
/**
|
||||
* A packet wrapper for PaperSpigot
|
||||
*/
|
||||
public class PaperPacket implements PacketProvider {
|
||||
|
||||
private final PlayerHandshakeEvent handshakeEvent;
|
||||
|
||||
public PaperPacket(PlayerHandshakeEvent handshakeEvent) {
|
||||
this.handshakeEvent = handshakeEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadString() {
|
||||
return handshakeEvent.getOriginalHandshake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPacketHostname(String hostname) throws PacketManipulationException {
|
||||
try {
|
||||
handshakeEvent.setCancelled(false);
|
||||
handshakeEvent.setServerHostname(hostname);
|
||||
} catch (Exception e) {
|
||||
throw new PacketManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper.handler;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import net.tcpshield.tcpshield.provider.PlayerProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PlayerManipulationException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A player wrapper for PaperSpigot
|
||||
*/
|
||||
public class PaperPlayer implements PlayerProvider {
|
||||
|
||||
private final PlayerHandshakeEvent handshakeEvent;
|
||||
|
||||
public PaperPlayer(PlayerHandshakeEvent handshakeEvent) {
|
||||
this.handshakeEvent = handshakeEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trys to grab the UUID of the handshake
|
||||
* @return If found, the corrosponding uuid, if not, unknown
|
||||
*/
|
||||
@Override
|
||||
public String getUUID() {
|
||||
UUID uuid = handshakeEvent.getUniqueId();
|
||||
if (uuid == null)
|
||||
return "unknown";
|
||||
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported with Paper handshakes
|
||||
* @return unknown
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return handshakeEvent.getSocketAddressHostname();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws PlayerManipulationException {
|
||||
try {
|
||||
handshakeEvent.setSocketAddressHostname(ip.getAddress().getHostAddress());
|
||||
} catch (Exception e) {
|
||||
throw new PlayerManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
handshakeEvent.setCancelled(false); // Caused issues with newer versions of Paper (Thanks https://github.com/realDragonium)
|
||||
// Paper requires a non-empty fail message (Guava checkArgument), so use a single space
|
||||
// to avoid the plugin echoing a visible kick reason back into the console.
|
||||
handshakeEvent.setFailMessage(" ");
|
||||
handshakeEvent.setFailed(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper.impl;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
|
||||
public class PaperPacketImpl implements IPacket {
|
||||
|
||||
private final PlayerHandshakeEvent handshakeEvent;
|
||||
|
||||
public PaperPacketImpl(PlayerHandshakeEvent handshakeEvent) {
|
||||
this.handshakeEvent = handshakeEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawPayload() {
|
||||
return handshakeEvent.getOriginalHandshake();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) {
|
||||
handshakeEvent.setCancelled(false);
|
||||
handshakeEvent.setServerHostname(hostname);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.paper.impl;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PaperPlayerImpl implements IPlayer {
|
||||
|
||||
private final PlayerHandshakeEvent handshakeEvent;
|
||||
|
||||
public PaperPlayerImpl(PlayerHandshakeEvent handshakeEvent) {
|
||||
this.handshakeEvent = handshakeEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUUID() {
|
||||
UUID uuid = handshakeEvent.getUniqueId();
|
||||
if (uuid == null) return "unknown";
|
||||
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return handshakeEvent.getSocketAddressHostname();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) {
|
||||
handshakeEvent.setSocketAddressHostname(ip.getAddress().getHostAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
handshakeEvent.setCancelled(false);
|
||||
handshakeEvent.setFailMessage("Connection failed. Please try again or contract an administrator.");
|
||||
handshakeEvent.setFailed(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib;
|
||||
|
||||
import net.tcpshield.tcpshield.bukkit.TCPShieldBukkit;
|
||||
import net.tcpshield.tcpshield.bukkit.protocollib.handler.ProtocolLibHandshakeHandler;
|
||||
import net.tcpshield.tcpshield.bukkit.provider.BukkitImplProvider;
|
||||
|
||||
/**
|
||||
* Bukkit provider for the ProtocolLib implementation of TCPShield
|
||||
*/
|
||||
public class BukkitProtocolLib extends BukkitImplProvider {
|
||||
|
||||
public BukkitProtocolLib(TCPShieldBukkit bukkitPlugin) {
|
||||
super(bukkitPlugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
ProtocolLibHandshakeHandler packetHandler = new ProtocolLibHandshakeHandler(this);
|
||||
com.comphenix.protocol.ProtocolLibrary.getProtocolManager().addPacketListener(packetHandler);
|
||||
}
|
||||
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import net.tcpshield.tcpshield.HandshakePacketHandler;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.bukkit.impl.BukkitConfigImpl;
|
||||
import net.tcpshield.tcpshield.bukkit.protocollib.impl.ProtocolLibPacketImpl;
|
||||
import net.tcpshield.tcpshield.bukkit.protocollib.impl.ProtocolLibPlayerImpl;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class ProtocolLibHandshakePacketHandler extends PacketAdapter {
|
||||
|
||||
private final HandshakePacketHandler handshakePacketHandler;
|
||||
|
||||
public ProtocolLibHandshakePacketHandler(JavaPlugin plugin) {
|
||||
super(plugin, ListenerPriority.HIGHEST, PacketType.Handshake.Client.SET_PROTOCOL);
|
||||
this.handshakePacketHandler = new HandshakePacketHandler(plugin.getLogger(), new BukkitConfigImpl(plugin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent e) {
|
||||
IPacket packet = new ProtocolLibPacketImpl(e.getPacket());
|
||||
IPlayer player = new ProtocolLibPlayerImpl(e.getPlayer());
|
||||
|
||||
handshakePacketHandler.onHandshake(packet, player);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class TCPShieldProtocolLib {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public TCPShieldProtocolLib(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
if (Bukkit.getPluginManager().getPlugin("ProtocolLib") == null) {
|
||||
plugin.getLogger().warning("TCPShield not loading because ProtocolLib is not installed. Either use Paper to enable native compatibility or install ProtocolLib.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProtocolLibHandshakePacketHandler packetHandler = new ProtocolLibHandshakePacketHandler(plugin);
|
||||
com.comphenix.protocol.ProtocolLibrary.getProtocolManager().addPacketListener(packetHandler);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib.handler;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import net.tcpshield.tcpshield.bukkit.provider.BukkitImplProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* The handshake handler for ProtocolLib
|
||||
*/
|
||||
public class ProtocolLibHandshakeHandler extends PacketAdapter {
|
||||
|
||||
private final BukkitImplProvider bukkitProvider;
|
||||
|
||||
public ProtocolLibHandshakeHandler(BukkitImplProvider bukkitProvider) {
|
||||
super(bukkitProvider.getPlugin(), ListenerPriority.LOWEST, PacketType.Handshake.Client.SET_PROTOCOL);
|
||||
this.bukkitProvider = bukkitProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent e) {
|
||||
ProtocolLibPacket packet = new ProtocolLibPacket(e.getPacket());
|
||||
ProtocolLibPlayer player = new ProtocolLibPlayer(e.getPlayer());
|
||||
|
||||
try {
|
||||
bukkitProvider.getPlugin().getPacketHandler().handleHandshake(packet, player);
|
||||
} catch (HandshakeException exception) {
|
||||
bukkitProvider.getPlugin().getDebugger().exception(exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib.handler;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import net.tcpshield.tcpshield.provider.PacketProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PacketManipulationException;
|
||||
|
||||
/**
|
||||
* A packet wrapper for ProtocolLib
|
||||
*/
|
||||
public class ProtocolLibPacket implements PacketProvider {
|
||||
|
||||
private final PacketContainer packetContainer;
|
||||
private final String rawPayload;
|
||||
|
||||
public ProtocolLibPacket(PacketContainer packetContainer) {
|
||||
this.packetContainer = packetContainer;
|
||||
this.rawPayload = packetContainer.getStrings().read(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadString() {
|
||||
return rawPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPacketHostname(String hostname) throws PacketManipulationException {
|
||||
try {
|
||||
packetContainer.getStrings().write(0, hostname);
|
||||
} catch (Exception e) {
|
||||
throw new PacketManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib.handler;
|
||||
|
||||
import com.comphenix.protocol.injector.temporary.MinimalInjector;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
||||
import net.tcpshield.tcpshield.provider.PlayerProvider;
|
||||
import net.tcpshield.tcpshield.util.ReflectionUtil;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PlayerManipulationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ReflectionException;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
/**
|
||||
* A player wrapper for ProtocolLib
|
||||
*/
|
||||
public class ProtocolLibPlayer implements PlayerProvider {
|
||||
|
||||
/*
|
||||
* Reflection objects needed for manipulation
|
||||
*/
|
||||
private static Class<?> abstractChannelClass;
|
||||
|
||||
static {
|
||||
try {
|
||||
abstractChannelClass = Class.forName("io.netty.channel.AbstractChannel");
|
||||
} catch (ClassNotFoundException e) {
|
||||
try {
|
||||
abstractChannelClass = Class.forName("net.minecraft.util.io.netty.channel.AbstractChannel");
|
||||
} catch (ClassNotFoundException e2) {
|
||||
throw new InitializationException(new ReflectionException(e2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Player player;
|
||||
private String ip;
|
||||
|
||||
public ProtocolLibPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.ip = player.getAddress().getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unsupported with ProtocolLib handshakes
|
||||
* @return unknown
|
||||
*/
|
||||
@Override
|
||||
public String getUUID() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported with ProtocolLib handshakes
|
||||
* @return unknown
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws PlayerManipulationException {
|
||||
try {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
|
||||
MinimalInjector ignored = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
Object injector = ReflectionUtil.getObjectInPrivateField(ignored, "injector");
|
||||
Object networkManager = ReflectionUtil.getObjectInPrivateField(injector, "networkManager");
|
||||
|
||||
ReflectionUtil.setFinalField(networkManager, ReflectionUtil.searchFieldByClass(networkManager.getClass(), SocketAddress.class), ip);
|
||||
|
||||
Object channel = ReflectionUtil.getObjectInPrivateField(injector, "wrappedChannel");
|
||||
ReflectionUtil.setFinalField(channel, ReflectionUtil.getDeclaredField(abstractChannelClass, "remoteAddress"), ip);
|
||||
} catch (Exception e) {
|
||||
throw new PlayerManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
player.kickPlayer("");
|
||||
}
|
||||
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib.impl;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
|
||||
public class ProtocolLibPacketImpl implements IPacket {
|
||||
|
||||
private final PacketContainer packetContainer;
|
||||
private final String rawPayload;
|
||||
|
||||
public ProtocolLibPacketImpl(PacketContainer packetContainer) {
|
||||
this.packetContainer = packetContainer;
|
||||
this.rawPayload = packetContainer.getStrings().read(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawPayload() {
|
||||
return rawPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) {
|
||||
packetContainer.getStrings().write(0, hostname);
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bukkit.protocollib.impl;
|
||||
|
||||
import com.comphenix.protocol.injector.server.SocketInjector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import io.netty.channel.AbstractChannel;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.exception.IPModificationFailureException;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public class ProtocolLibPlayerImpl implements IPlayer {
|
||||
|
||||
private static Field socketAddressField;
|
||||
private final Player player;
|
||||
private String ip;
|
||||
|
||||
public ProtocolLibPlayerImpl(Player player) {
|
||||
this.player = player;
|
||||
this.ip = player.getAddress().getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUUID() {
|
||||
return "unknown"; // not supported with temporary players
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown"; // not supported with temporary players
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws IPModificationFailureException {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
|
||||
try {
|
||||
SocketInjector ignored = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
Object injector = ReflectionUtils.getObjectInPrivateField(ignored, "injector");
|
||||
Object networkManager = ReflectionUtils.getObjectInPrivateField(injector, "networkManager");
|
||||
if (socketAddressField == null) {
|
||||
socketAddressField = ReflectionUtils.searchFieldByClass(networkManager.getClass(), SocketAddress.class);
|
||||
}
|
||||
|
||||
ReflectionUtils.setFinalField(networkManager, socketAddressField, ip);
|
||||
|
||||
Object channel = ReflectionUtils.getObjectInPrivateField(injector, "originalChannel");
|
||||
ReflectionUtils.setFinalField(channel, ReflectionUtils.getDeclaredField(AbstractChannel.class, "remoteAddress"), ip);
|
||||
} catch (Exception e) {
|
||||
throw new IPModificationFailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
player.kickPlayer("");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.tcpshield.tcpshield.bukkit.provider;
|
||||
|
||||
import net.tcpshield.tcpshield.bukkit.TCPShieldBukkit;
|
||||
|
||||
/**
|
||||
* A provider for Bukkit implementations of TCPShield
|
||||
*/
|
||||
public abstract class BukkitImplProvider {
|
||||
|
||||
private final TCPShieldBukkit bukkitPlugin;
|
||||
|
||||
public BukkitImplProvider(TCPShieldBukkit bukkitPlugin) {
|
||||
this.bukkitPlugin = bukkitPlugin;
|
||||
}
|
||||
|
||||
|
||||
public TCPShieldBukkit getPlugin() {
|
||||
return bukkitPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Bukkit provider
|
||||
*/
|
||||
public abstract void load();
|
||||
|
||||
|
||||
/**
|
||||
* Checks for the Paper handshake event class for the Paper implementation
|
||||
* @return Boolean stating if the runtime has the proper Paper event
|
||||
*/
|
||||
public static boolean hasPaperEvent() {
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.event.player.PlayerHandshakeEvent");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bungee;
|
||||
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.PlayerHandshakeEvent;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.tcpshield.tcpshield.HandshakePacketHandler;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.bungee.impl.BungeeConfigImpl;
|
||||
import net.tcpshield.tcpshield.bungee.impl.BungeePacketImpl;
|
||||
import net.tcpshield.tcpshield.bungee.impl.BungeePlayerImpl;
|
||||
|
||||
public class BungeeHandshakePacketHandler implements Listener {
|
||||
|
||||
private final HandshakePacketHandler handshakePacketHandler;
|
||||
|
||||
public BungeeHandshakePacketHandler(Plugin plugin) {
|
||||
this.handshakePacketHandler = new HandshakePacketHandler(plugin.getLogger(), new BungeeConfigImpl(plugin));
|
||||
}
|
||||
|
||||
@EventHandler(priority = -64)
|
||||
public void onProxyPingEvent(ProxyPingEvent e) {
|
||||
PendingConnection connection = e.getConnection();
|
||||
if (!connection.isLegacy()) return;
|
||||
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
@EventHandler(priority = -64)
|
||||
public void onPlayerHandshake(PlayerHandshakeEvent e) {
|
||||
IPacket packet = new BungeePacketImpl(e.getHandshake(), e.getConnection());
|
||||
IPlayer player = new BungeePlayerImpl(e.getConnection());
|
||||
|
||||
handshakePacketHandler.onHandshake(packet, player);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bungee;
|
||||
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TCPShieldBungee extends Plugin implements Listener {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
PluginManager pluginManager = this.getProxy().getPluginManager();
|
||||
pluginManager.registerListener(this, new BungeeHandshakePacketHandler(this));
|
||||
|
||||
Logger logger = ProxyServer.getInstance().getLogger();
|
||||
Logger newLogger = new Logger("BungeeCord", null) {
|
||||
public void log(Level level, String msg, Object param1) {
|
||||
if ((msg.equals("{0} has connected")) && (param1.getClass().getSimpleName().equals("InitialHandler"))) {
|
||||
return;
|
||||
}
|
||||
super.log(level, msg, param1);
|
||||
}
|
||||
};
|
||||
|
||||
newLogger.setParent(logger);
|
||||
try {
|
||||
ReflectionUtils.setField(ProxyServer.getInstance(), "logger", newLogger);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bungee.impl;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class BungeeConfigImpl extends TCPShieldConfig {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
public BungeeConfigImpl(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
saveDefaultConfig();
|
||||
|
||||
Configuration config = loadConfig();
|
||||
this.onlyProxy = config.getBoolean("only-allow-proxy-connections");
|
||||
this.timestampValidationMode = config.getString("timestamp-validation");
|
||||
this.debug = config.getBoolean("debug-mode");
|
||||
}
|
||||
|
||||
private void saveDefaultConfig() {
|
||||
File dataFolder = plugin.getDataFolder();
|
||||
if (!dataFolder.exists()) dataFolder.mkdir();
|
||||
|
||||
File file = getConfigFile();
|
||||
if (file.exists()) return;
|
||||
|
||||
try (InputStream in = plugin.getResourceAsStream("config.yml")) {
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private File getConfigFile() {
|
||||
File dataFolder = plugin.getDataFolder();
|
||||
return new File(dataFolder, "config.yml");
|
||||
}
|
||||
|
||||
private Configuration loadConfig() {
|
||||
try {
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
File configFile = getConfigFile();
|
||||
return configProvider.load(configFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Couldn't load config", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bungee.impl;
|
||||
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class BungeePacketImpl implements IPacket {
|
||||
|
||||
private final Handshake handshake;
|
||||
private final PendingConnection pendingConnection;
|
||||
|
||||
public BungeePacketImpl(Handshake handshake, PendingConnection pendingConnection) {
|
||||
this.handshake = handshake;
|
||||
this.pendingConnection = pendingConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawPayload() {
|
||||
return handshake.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) throws Exception {
|
||||
InetSocketAddress virtualHost = InetSocketAddress.createUnresolved(hostname, handshake.getPort());
|
||||
try {
|
||||
ReflectionUtils.setFinalField(pendingConnection, "virtualHost", virtualHost);
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.setFinalField(pendingConnection, "vHost", virtualHost);
|
||||
}
|
||||
|
||||
ReflectionUtils.setField(handshake, "host", hostname);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package net.tcpshield.tcpshield.bungee.impl;
|
||||
|
||||
import io.netty.channel.AbstractChannel;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.exception.IPModificationFailureException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeePlayerImpl implements IPlayer {
|
||||
|
||||
private final PendingConnection pendingConnection;
|
||||
private String ip;
|
||||
|
||||
public BungeePlayerImpl(PendingConnection pendingConnection) {
|
||||
this.pendingConnection = pendingConnection;
|
||||
this.ip = pendingConnection.getAddress().getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUUID() {
|
||||
UUID uuid = pendingConnection.getUniqueId();
|
||||
if (uuid == null) return "Unknown";
|
||||
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return pendingConnection.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws IPModificationFailureException {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
|
||||
try {
|
||||
Object channelWrapper = ReflectionUtils.getObjectInPrivateField(pendingConnection, "ch");
|
||||
Object channel = ReflectionUtils.getObjectInPrivateField(channelWrapper, "ch");
|
||||
|
||||
ReflectionUtils.setFinalField(channelWrapper, "remoteAddress", ip);
|
||||
ReflectionUtils.setFinalField(channel, ReflectionUtils.getPrivateField(AbstractChannel.class, "remoteAddress"), ip);
|
||||
ReflectionUtils.setFinalField(channel, ReflectionUtils.getPrivateField(AbstractChannel.class, "localAddress"), ip);
|
||||
} catch (Exception e) {
|
||||
throw new IPModificationFailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
pendingConnection.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class ConnectionNotProxiedException extends SecurityException {
|
||||
|
||||
public ConnectionNotProxiedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConnectionNotProxiedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ConnectionNotProxiedException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class IPModificationFailureException extends Exception {
|
||||
|
||||
public IPModificationFailureException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class InvalidTimestampException extends SigningVerificationFailureException {
|
||||
|
||||
private final long timestamp;
|
||||
private final long currentTime;
|
||||
|
||||
public InvalidTimestampException(long timestamp, long currentTime) {
|
||||
this.timestamp = timestamp;
|
||||
this.currentTime = currentTime;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public long getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class MalformedPayloadException extends ConnectionNotProxiedException {
|
||||
|
||||
public MalformedPayloadException() {
|
||||
}
|
||||
|
||||
public MalformedPayloadException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public MalformedPayloadException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class SigningVerificationFailureException extends MalformedPayloadException {
|
||||
|
||||
public SigningVerificationFailureException() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.tcpshield.tcpshield.exception;
|
||||
|
||||
public class TCPShieldInitializationException extends RuntimeException {
|
||||
|
||||
public TCPShieldInitializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.tcpshield.tcpshield.geyser;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class GeyserHandshakeHandler {
|
||||
|
||||
private final TCPShieldPlugin plugin;
|
||||
|
||||
public GeyserHandshakeHandler(TCPShieldPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
InstanceHolder.getHandshakeHandlers().addHandshakeHandler(data -> {
|
||||
if (data.getIp() == null) {
|
||||
// not bedrock
|
||||
this.plugin.getDebugger().warn("Connection with no bedrock data joined and ignored: username = %s, hostname = %s", data.getCorrectUsername(), data.getHostname());
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getDebugger().warn("Bedrock connection joined and validated: username = %s, hostname = %s", data.getCorrectUsername(), data.getHostname());
|
||||
|
||||
String oldPayload = data.getHostname();
|
||||
if (oldPayload.contains("///")) {
|
||||
oldPayload = oldPayload.split("///")[0];
|
||||
}
|
||||
|
||||
String hostname = oldPayload;
|
||||
String realIp = data.getIp();
|
||||
String timestamp = "0";
|
||||
String signature = GeyserUtils.SESSION_SECRET;
|
||||
|
||||
// so floodgate likes to append "\0Floodgate\0" or something stupid to the hostname
|
||||
// which just completely breaks this, so we have to pass these in a different
|
||||
// order to stop bungeecord detecting it as FML data, thanks floodgate.
|
||||
String newHostname = realIp + ":0///" + signature + "///" + timestamp + "///" + hostname;
|
||||
if (hostname.contains("\0")) { // this is usually fine because FML, providing the hostname is first
|
||||
this.plugin.getDebugger().warn("Hostname contains null byte: " + Arrays.toString(hostname.toCharArray()));
|
||||
}
|
||||
|
||||
this.plugin.getDebugger().warn("Setting hostname to %s - %s", newHostname, Arrays.toString(newHostname.toCharArray()));
|
||||
data.setHostname(newHostname);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.tcpshield.tcpshield.geyser;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserUtils {
|
||||
|
||||
public static final String SESSION_SECRET = UUID.randomUUID().toString();
|
||||
public static boolean GEYSER_SUPPORT_ENABLED = false;
|
||||
|
||||
public static void initGeyser(TCPShieldPlugin plugin, ConfigProvider config) {
|
||||
if (!(GEYSER_SUPPORT_ENABLED = useGeyser(config))) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeyserHandshakeHandler geyserHandler = new GeyserHandshakeHandler(plugin);
|
||||
geyserHandler.init();
|
||||
}
|
||||
|
||||
private static boolean useGeyser(ConfigProvider config) {
|
||||
if (!config.isGeyser()) return false;
|
||||
|
||||
try {
|
||||
Class.forName("org.geysermc.floodgate.api.InstanceHolder");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package net.tcpshield.tcpshield.provider;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigLoadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigReloadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigResetException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An abstract provider for TCPShield's options.
|
||||
*/
|
||||
public abstract class ConfigProvider {
|
||||
|
||||
/*
|
||||
* Configuration options
|
||||
*/
|
||||
protected boolean onlyProxy = true;
|
||||
protected String timestampValidationMode = "htpdate";
|
||||
protected boolean doDebug = true; // Fail-safe default set to true
|
||||
protected boolean geyser = false;
|
||||
protected boolean velocityHandlePreLoginEvent = true;
|
||||
|
||||
// spigot/paper only option
|
||||
protected boolean preferProtocolLib;
|
||||
|
||||
protected File dataFolder;
|
||||
protected File configFile;
|
||||
|
||||
public boolean isOnlyProxy() {
|
||||
return this.onlyProxy;
|
||||
}
|
||||
|
||||
public boolean isGeyser() {
|
||||
return this.geyser;
|
||||
}
|
||||
|
||||
public boolean handlePreLoginEvent() {
|
||||
return this.velocityHandlePreLoginEvent;
|
||||
}
|
||||
|
||||
public String getTimestampValidationMode() {
|
||||
return this.timestampValidationMode;
|
||||
}
|
||||
|
||||
public boolean doDebug() {
|
||||
return this.doDebug;
|
||||
}
|
||||
|
||||
public boolean preferProtocolLib() {
|
||||
return this.preferProtocolLib;
|
||||
}
|
||||
|
||||
public File getDataFolder() {
|
||||
return dataFolder;
|
||||
}
|
||||
|
||||
public File getConfigFile() {
|
||||
return configFile;
|
||||
}
|
||||
|
||||
/*
|
||||
* Plugin Constants
|
||||
*/
|
||||
protected final long maxTimestampDifference = 3; // In Unix Timesteps (Seconds)
|
||||
|
||||
|
||||
public long getMaxTimestampDifference() {
|
||||
return maxTimestampDifference;
|
||||
}
|
||||
|
||||
/*
|
||||
* Required methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deletes the current config saved to the disk and reinstalls the default config
|
||||
* @throws ConfigResetException Thrown if resetting fails
|
||||
*/
|
||||
protected abstract void reset() throws ConfigResetException;
|
||||
|
||||
/**
|
||||
* Trys to load the options from the config, if failed, throws ConfigLoadException
|
||||
* @throws ConfigLoadException Thrown if loading fails, reset should be called if thrown
|
||||
*/
|
||||
protected abstract void load() throws ConfigLoadException;
|
||||
|
||||
/**
|
||||
* Trys to reload the config, if failed, throws ConfigReloadException
|
||||
* @throws ConfigReloadException Thrown if reloading fails
|
||||
*/
|
||||
public abstract void reload() throws ConfigReloadException;
|
||||
|
||||
/**
|
||||
* Checks the provided nodes to see if they exist in the config
|
||||
* @param nodes The nodes to check
|
||||
* @throws ConfigException Thrown when a node isnt found
|
||||
*/
|
||||
protected abstract void checkNodes(String... nodes) throws ConfigException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.tcpshield.tcpshield.provider;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PacketManipulationException;
|
||||
|
||||
/**
|
||||
* A packet provider for TCPShield handling
|
||||
*/
|
||||
public interface PacketProvider {
|
||||
|
||||
/**
|
||||
* Gets the raw payload from the packet
|
||||
* @return The raw payload in the form of a String
|
||||
*/
|
||||
String getPayloadString();
|
||||
|
||||
/**
|
||||
* Sets the hostname within the packet
|
||||
* @param hostname The new hostname
|
||||
*/
|
||||
void setPacketHostname(String hostname) throws PacketManipulationException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.tcpshield.tcpshield.provider;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PlayerManipulationException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* A player provider for TCPShield handling
|
||||
*/
|
||||
public interface PlayerProvider {
|
||||
|
||||
String getUUID();
|
||||
|
||||
String getName();
|
||||
|
||||
String getIP();
|
||||
|
||||
/**
|
||||
* Sets the players IP address directly to the server
|
||||
*
|
||||
* @param ip The new IP address to pass to the proxy/server
|
||||
*/
|
||||
void setIP(InetSocketAddress ip) throws PlayerManipulationException;
|
||||
|
||||
void disconnect();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.tcpshield.tcpshield.util;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A util for debugging
|
||||
*/
|
||||
public abstract class Debugger {
|
||||
|
||||
/**
|
||||
* Creates a debugger instance according to
|
||||
* the plugin's configuration options
|
||||
* @param plugin The TCPShield plugin
|
||||
* @return A debugger instance from plugin's configuration
|
||||
*/
|
||||
public static Debugger createDebugger(TCPShieldPlugin plugin) {
|
||||
if (plugin.getConfigProvider().doDebug())
|
||||
/*
|
||||
* A non-empty debugger using the plugin's logger
|
||||
*/
|
||||
return new Debugger(plugin.getLogger()) {
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... formats) {
|
||||
this.logger.info("Debug : " + String.format(format, formats));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... formats) {
|
||||
this.logger.warning("Debug : " + String.format(format, formats));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object... formats) {
|
||||
this.logger.severe("Debug : " + String.format(format, formats));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
};
|
||||
else
|
||||
/*
|
||||
* An empty debugger
|
||||
*/
|
||||
return new Debugger(null) {
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... formats) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... formats) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object... formats) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(Exception exception) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected final Logger logger;
|
||||
|
||||
/**
|
||||
* Non-accessable constructor for creating a debugger
|
||||
* @param logger The plugin's logger
|
||||
*/
|
||||
private Debugger(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs debug information with log level "INFO"
|
||||
* @param format The output string to be formatted
|
||||
* @param formats The formarts for the string
|
||||
*/
|
||||
public abstract void info(String format, Object... formats);
|
||||
|
||||
/**
|
||||
* Outputs debug information with log level "WARNING"
|
||||
* @param format The output string to be formatted
|
||||
* @param formats The formarts for the string
|
||||
*/
|
||||
public abstract void warn(String format, Object... formats);
|
||||
|
||||
/**
|
||||
* Outputs debug information with log level "SEVERE"
|
||||
* @param format The output string to be formatted
|
||||
* @param formats The formarts for the string
|
||||
*/
|
||||
public abstract void error(String format, Object... formats);
|
||||
|
||||
/**
|
||||
* Outputs the exception through the debugger
|
||||
* @param exception The exception to be outputted
|
||||
*/
|
||||
public abstract void exception(Exception exception);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package net.tcpshield.tcpshield.util;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ReflectionException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* A util for reflection and manipulation
|
||||
*/
|
||||
public class ReflectionUtil {
|
||||
|
||||
/**
|
||||
* Cached information for Fields
|
||||
*/
|
||||
private static final Table<Class<?>, String, Field> CACHED_FIELDS_BY_NAME = HashBasedTable.create();
|
||||
private static final Table<Class<?>, Class<?>, Field> CACHED_FIELDS_BY_CLASS = HashBasedTable.create();
|
||||
// private static Field modifiersField;
|
||||
|
||||
|
||||
/**
|
||||
* Sets a final field, with a field name, inside an object
|
||||
* @param object The object
|
||||
* @param fieldName The field name
|
||||
* @param value The new value
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static void setFinalField(Object object, String fieldName, Object value) throws ReflectionException {
|
||||
setFinalField(object, getPrivateField(object.getClass(), fieldName), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a final field, with a field object, inside an object
|
||||
* @param object The object
|
||||
* @param field The field
|
||||
* @param value The new value
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static void setFinalField(Object object, Field field, Object value) throws ReflectionException {
|
||||
field.setAccessible(true);
|
||||
|
||||
// Removed due to complications with Velocity (Thanks https://github.com/AoElite)
|
||||
|
||||
// if (Modifier.isFinal(field.getModifiers())) {
|
||||
// if (modifiersField == null) {
|
||||
// try {
|
||||
// modifiersField = getDeclaredField(Field.class, "modifiers");
|
||||
// } catch (ReflectionException e) { // workaround for when searching for the modifiers field on Java 12 or higher
|
||||
// try {
|
||||
// Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
|
||||
// getDeclaredFields0.setAccessible(true);
|
||||
//
|
||||
// Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
|
||||
// modifiersField = Arrays.stream(fields).filter(modifier -> modifier.getName().equals("modifiers")).findFirst().orElseThrow(() -> new ReflectionException("Could not find the modifiers field"));
|
||||
// } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e2) {
|
||||
// throw new ReflectionException(e2);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// modifiersField.setAccessible(true);
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
// } catch (IllegalAccessException e) {
|
||||
// throw new ReflectionException(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
setField(object, field, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field, with a field name, inside an object
|
||||
* @param object The object
|
||||
* @param fieldName The field name
|
||||
* @param value The new value
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static void setField(Object object, String fieldName, Object value) throws ReflectionException {
|
||||
setField(object, getPrivateField(object.getClass(), fieldName), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field, with a field object, inside an object
|
||||
* @param object The object
|
||||
* @param field The field
|
||||
* @param value The new value
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static void setField(Object object, Field field, Object value) throws ReflectionException {
|
||||
try {
|
||||
field.set(object, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ReflectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object inside a private field, with a field name, in an object
|
||||
* @param object The object
|
||||
* @param fieldName The field name
|
||||
* @return The grabbed object
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static Object getObjectInPrivateField(Object object, String fieldName) throws ReflectionException {
|
||||
Field field = getPrivateField(object.getClass(), fieldName);
|
||||
try {
|
||||
return field.get(object);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ReflectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a private field, with a field name, inside an object
|
||||
* @param clazz The clazz
|
||||
* @param fieldName The field name
|
||||
* @return The grabbed field
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static Field getPrivateField(Class<?> clazz, String fieldName) throws ReflectionException {
|
||||
Field field = getDeclaredField(clazz, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a field, with the type of the provided class, inside a class
|
||||
* @param clazz The class to search through
|
||||
* @param searchFor The class type to search for
|
||||
* @return The found field
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static Field searchFieldByClass(Class<?> clazz, Class<?> searchFor) throws ReflectionException {
|
||||
Field cachedField = CACHED_FIELDS_BY_CLASS.get(clazz, searchFor);
|
||||
if (cachedField != null) return cachedField;
|
||||
|
||||
Class<?> currentClass = clazz;
|
||||
do {
|
||||
for (Field field : currentClass.getDeclaredFields()) {
|
||||
if (!searchFor.isAssignableFrom(field.getType())) continue;
|
||||
|
||||
CACHED_FIELDS_BY_CLASS.put(clazz, searchFor, field);
|
||||
return field;
|
||||
}
|
||||
|
||||
currentClass = currentClass.getSuperclass();
|
||||
} while (currentClass != null);
|
||||
|
||||
throw new ReflectionException("no " + searchFor.getName() + " field for clazz = " + clazz.getName() + " found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a declared field, with a field name, inside a class
|
||||
* @param clazz The clazz
|
||||
* @param fieldName The field name
|
||||
* @return The declared field
|
||||
* @throws ReflectionException Thrown when the operation was not successful
|
||||
*/
|
||||
public static Field getDeclaredField(Class<?> clazz, String fieldName) throws ReflectionException {
|
||||
Field cachedField = CACHED_FIELDS_BY_NAME.get(clazz, fieldName);
|
||||
if (cachedField != null) return cachedField;
|
||||
|
||||
Field field;
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Class<?> superclass = clazz.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return getDeclaredField(superclass, fieldName);
|
||||
} else {
|
||||
throw new ReflectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
CACHED_FIELDS_BY_NAME.put(clazz, fieldName, field);
|
||||
return field;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.tcpshield.tcpshield.util.exception;
|
||||
|
||||
/**
|
||||
* Outline for TCPShield exceptions
|
||||
*/
|
||||
public abstract class TCPShieldException extends RuntimeException {
|
||||
|
||||
public TCPShieldException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public TCPShieldException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public TCPShieldException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public TCPShieldException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.config;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the loading phase of the config process
|
||||
*/
|
||||
public class ConfigLoadException extends ConfigException {
|
||||
|
||||
public ConfigLoadException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigLoadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public ConfigLoadException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigLoadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.config;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the reloading phase of the config process
|
||||
*/
|
||||
public class ConfigReloadException extends ConfigException {
|
||||
|
||||
public ConfigReloadException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigReloadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public ConfigReloadException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigReloadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.config;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the resetting phase of the config process
|
||||
*/
|
||||
public class ConfigResetException extends ConfigException {
|
||||
|
||||
public ConfigResetException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigResetException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public ConfigResetException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigResetException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.manipulate;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* An exception thrown during packet manipulation
|
||||
*/
|
||||
public class PacketManipulationException extends HandshakeException {
|
||||
|
||||
public PacketManipulationException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public PacketManipulationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public PacketManipulationException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public PacketManipulationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.manipulate;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* An exception thrown during player manipulation
|
||||
*/
|
||||
public class PlayerManipulationException extends HandshakeException {
|
||||
|
||||
public PlayerManipulationException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public PlayerManipulationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public PlayerManipulationException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public PlayerManipulationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.parse;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* An exception thrown when a handshake packet has invalid payload data
|
||||
*/
|
||||
public class InvalidPayloadException extends HandshakeException {
|
||||
|
||||
public InvalidPayloadException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
|
||||
public InvalidPayloadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public InvalidPayloadException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public InvalidPayloadException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package net.tcpshield.tcpshield.util.exception.parse;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* An exception thrown when a handshake packet failed signature validation
|
||||
*/
|
||||
public class SignatureValidationException extends HandshakeException {
|
||||
|
||||
public SignatureValidationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package net.tcpshield.tcpshield.util.exception.parse;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
import net.tcpshield.tcpshield.util.validation.timestamp.TimestampValidator;
|
||||
|
||||
/**
|
||||
* An exception thrown when a handshake packet failed timestamp validation
|
||||
*/
|
||||
public class TimestampValidationException extends HandshakeException {
|
||||
|
||||
private final TimestampValidator timestampValidator;
|
||||
private final long timestamp;
|
||||
|
||||
public TimestampValidationException(TimestampValidator timestampValidator, long timestamp) {
|
||||
super();
|
||||
|
||||
this.timestampValidator = timestampValidator;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
public TimestampValidator getTimestampValidator() {
|
||||
return timestampValidator;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the CIDR process of TCPShield
|
||||
*/
|
||||
public class CIDRException extends TCPShieldException {
|
||||
|
||||
public CIDRException(Throwable throwable) {
|
||||
super("An exception occured during the CIDR process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public CIDRException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public CIDRException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public CIDRException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the config loading, reloeading, saving, etc. process of TCPShield
|
||||
*/
|
||||
public class ConfigException extends TCPShieldException {
|
||||
|
||||
public ConfigException(Throwable throwable) {
|
||||
super("An exception occured during the config process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public ConfigException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public ConfigException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the handshake handling of TCPShield
|
||||
*/
|
||||
public class HandshakeException extends TCPShieldException {
|
||||
|
||||
public HandshakeException(Throwable throwable) {
|
||||
super("An exception occured during the handshake process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public HandshakeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public HandshakeException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public HandshakeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
/**
|
||||
* An exception thrown during the initialization phase of TCPShield
|
||||
*/
|
||||
public class InitializationException extends TCPShieldException {
|
||||
|
||||
public InitializationException(Throwable throwable) {
|
||||
super("An exception occured during the initalization process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public InitializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public InitializationException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public InitializationException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
public class InvalidSecretException extends TCPShieldException {
|
||||
|
||||
public InvalidSecretException(Throwable throwable) {
|
||||
super("An invalid secret was provided during the geyser handshake process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public InvalidSecretException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public InvalidSecretException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public InvalidSecretException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.tcpshield.tcpshield.util.exception.phase;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.TCPShieldException;
|
||||
|
||||
/**
|
||||
* An exception thrown during reflection operations for TCPShield
|
||||
*/
|
||||
public class ReflectionException extends TCPShieldException {
|
||||
|
||||
public ReflectionException(Throwable throwable) {
|
||||
super("An exception occured during the reflection process", throwable);
|
||||
}
|
||||
|
||||
|
||||
public ReflectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public ReflectionException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
|
||||
public ReflectionException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.tcpshield.tcpshield.util.validation;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* A signature validator using the TCPShield public key
|
||||
*/
|
||||
public class SignatureValidator {
|
||||
|
||||
private final PublicKey publicKey;
|
||||
|
||||
public SignatureValidator() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
byte[] encodedKey = ByteStreams.toByteArray(SignatureValidator.class.getResourceAsStream("/signing_pub.key"));
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey);
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
||||
publicKey = keyFactory.generatePublic(keySpec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates a String and Signature pair
|
||||
* @param str The data in the form of a string
|
||||
* @param signature The provided signature
|
||||
* @return Boolean stating if it's a valid signature
|
||||
*/
|
||||
public boolean validate(String str, String signature) {
|
||||
return validate(str.getBytes(StandardCharsets.UTF_8), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a byte[] and Signature pair
|
||||
* @param data The data in the form of a byte array
|
||||
* @param signature The provided signature
|
||||
* @return Boolean stating if it's a valid signature
|
||||
*/
|
||||
private boolean validate(byte[] data, String signature) {
|
||||
try {
|
||||
byte[] decodedSignature = Base64.getDecoder().decode(signature);
|
||||
|
||||
Signature sig = Signature.getInstance("SHA512withECDSA");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data);
|
||||
|
||||
return sig.verify(decodedSignature);
|
||||
} catch (IllegalArgumentException | SignatureException | NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package net.tcpshield.tcpshield.util.validation.cidr;
|
||||
|
||||
import net.tcpshield.tcpshield.util.exception.phase.CIDRException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* CIDR matcher for the CIDR validator
|
||||
*/
|
||||
public abstract class CIDRMatcher {
|
||||
|
||||
/**
|
||||
* Creates a CIDRMatcher from a CIDR matching regex
|
||||
* @param cidrMatchString The string containing the CIDR matching regex
|
||||
* @return The corrosponding CIDRMatcher
|
||||
*/
|
||||
public static CIDRMatcher create(String cidrMatchString) {
|
||||
return new CIDRMatcher(cidrMatchString) {
|
||||
@Override
|
||||
public boolean match(InetAddress inetAddress) {
|
||||
return super.match(inetAddress);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private final int maskBits;
|
||||
private final int maskBytes;
|
||||
private final boolean simpleCIDR;
|
||||
private final InetAddress cidrAddress;
|
||||
|
||||
private CIDRMatcher(String cidrMatchString) {
|
||||
String[] split = cidrMatchString.split("/");
|
||||
|
||||
String parsedIPAddress;
|
||||
if (split.length != 0) {
|
||||
parsedIPAddress = split[0];
|
||||
|
||||
this.maskBits = Integer.parseInt(split[1]);
|
||||
this.simpleCIDR = maskBits == 32;
|
||||
} else {
|
||||
parsedIPAddress = cidrMatchString;
|
||||
|
||||
this.maskBits = -1;
|
||||
this.simpleCIDR = true;
|
||||
}
|
||||
|
||||
this.maskBytes = simpleCIDR ? -1 : maskBits / 8;
|
||||
|
||||
try {
|
||||
cidrAddress = InetAddress.getByName(parsedIPAddress);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new CIDRException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the provided InetAddress with the CIDR for a match
|
||||
* @param inetAddress The InetAddress to match with the CIDR
|
||||
* @return Boolean stating if the provided InetAddress matches the CIDR
|
||||
*/
|
||||
public boolean match(InetAddress inetAddress) {
|
||||
if (!cidrAddress.getClass().equals(inetAddress.getClass())) return false; // check if IP is IPv4 or IPv6
|
||||
|
||||
if (simpleCIDR) return inetAddress.equals(cidrAddress); // check for equality if it's a simple CIDR
|
||||
|
||||
byte[] inetAddressBytes = inetAddress.getAddress();
|
||||
byte[] requiredAddressBytes = cidrAddress.getAddress();
|
||||
|
||||
byte finalByte = (byte) (0xFF00 >> (maskBits & 0x07));
|
||||
|
||||
for (int i = 0; i < maskBytes; i++) {
|
||||
if (inetAddressBytes[i] != requiredAddressBytes[i]) return false;
|
||||
}
|
||||
|
||||
if (finalByte != 0)
|
||||
return (inetAddressBytes[maskBytes] & finalByte) == (requiredAddressBytes[maskBytes] & finalByte);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package net.tcpshield.tcpshield.util.validation.cidr;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.CIDRException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A CIDR validator for TCPShield
|
||||
*/
|
||||
public class CIDRValidator {
|
||||
|
||||
private final TCPShieldPlugin plugin;
|
||||
|
||||
private final File ipWhitelistFolder;
|
||||
|
||||
private final List<CIDRMatcher> cidrMatchers;
|
||||
private final Set<String> cache = new HashSet<>(); // Max potential size is equilivent to the amount of whitelisted IP's
|
||||
|
||||
public CIDRValidator(TCPShieldPlugin plugin) throws CIDRException {
|
||||
this.plugin = plugin;
|
||||
|
||||
ipWhitelistFolder = new File(plugin.getConfigProvider().getDataFolder(), "ip-whitelist");
|
||||
if (!ipWhitelistFolder.exists())
|
||||
ipWhitelistFolder.mkdir();
|
||||
|
||||
try {
|
||||
List<String> whitelists = loadWhitelists();
|
||||
cidrMatchers = loadCIDRMatchers(whitelists);
|
||||
} catch (Exception e) {
|
||||
throw new CIDRException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<CIDRMatcher> loadCIDRMatchers(List<String> whitelists) {
|
||||
List<CIDRMatcher> matchers = new ArrayList<>();
|
||||
|
||||
for (String whitelist : whitelists)
|
||||
try {
|
||||
matchers.add(CIDRMatcher.create(whitelist));
|
||||
} catch (Exception e) {
|
||||
plugin.getDebugger().warn("Exception occured while creating CIDRMatcher for \"%s\". Ignoring it.", whitelist);
|
||||
plugin.getDebugger().exception(e);
|
||||
}
|
||||
|
||||
return matchers;
|
||||
}
|
||||
|
||||
private List<String> loadWhitelists() throws FileNotFoundException {
|
||||
List<String> whitelists = new ArrayList<>();
|
||||
|
||||
for (File file : Objects.requireNonNull(ipWhitelistFolder.listFiles())) {
|
||||
if (file.isDirectory())
|
||||
continue;
|
||||
|
||||
try (Scanner scanner = new Scanner(file)) {
|
||||
while (scanner.hasNextLine()) {
|
||||
String cidrEntry = scanner.nextLine();
|
||||
whitelists.add(cidrEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return whitelists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an InetAddress with CIDR matchers
|
||||
* @param inetAddress The InetAddress to validate
|
||||
* @return Boolean stating if the InetAddress is validated with CIDR
|
||||
*/
|
||||
public boolean validate(InetAddress inetAddress) {
|
||||
String ip = inetAddress.getHostAddress();
|
||||
|
||||
if (cache.contains(ip))
|
||||
return true;
|
||||
|
||||
for (CIDRMatcher cidrMatcher : cidrMatchers)
|
||||
if (cidrMatcher.match(inetAddress)) {
|
||||
cache.add(ip);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package net.tcpshield.tcpshield.util.validation.timestamp;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
|
||||
/**
|
||||
* Base for timestamp validators
|
||||
*/
|
||||
public abstract class TimestampValidator {
|
||||
|
||||
/**
|
||||
* Creates an empty validator that always returns true
|
||||
* @return An empty validator always returning true
|
||||
*/
|
||||
public static TimestampValidator createEmpty(TCPShieldPlugin plugin) {
|
||||
return new TimestampValidator(plugin) {
|
||||
@Override
|
||||
public boolean validate(long timestamp) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a system validator that uses default validation
|
||||
* @return A system validator
|
||||
*/
|
||||
public static TimestampValidator createDefault(TCPShieldPlugin plugin) {
|
||||
return new TimestampValidator(plugin) {
|
||||
// Uses default methods
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
protected final TCPShieldPlugin plugin;
|
||||
|
||||
public TimestampValidator(TCPShieldPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates given timestamp to see if it's within a reasonable
|
||||
* time range
|
||||
* @param timestamp The timestamp
|
||||
* @return Boolean stating if its valid
|
||||
*/
|
||||
public boolean validate(long timestamp) {
|
||||
return Math.abs(getUnixTime() - timestamp) <= plugin.getConfigProvider().getMaxTimestampDifference();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the validator's current Unix time
|
||||
* @return The current time in Unix format
|
||||
* @default Returns the system time in Unix format
|
||||
*/
|
||||
public long getUnixTime() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package net.tcpshield.tcpshield.util.validation.timestamp.impl;
|
||||
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import net.tcpshield.tcpshield.util.validation.timestamp.TimestampValidator;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* A timestamp validator that offsets system time with Google's to sync
|
||||
* tiemstamps
|
||||
*/
|
||||
public class HTPDateTimestampValidator extends TimestampValidator {
|
||||
|
||||
private volatile long htpDateOffset = 0;
|
||||
|
||||
public HTPDateTimestampValidator(TCPShieldPlugin plugin) {
|
||||
super(plugin);
|
||||
|
||||
ForkJoinPool.commonPool().execute(() -> {
|
||||
try {
|
||||
updateHTPDateOffset();
|
||||
} catch (Exception e) {
|
||||
throw new InitializationException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void updateHTPDateOffset() throws IOException {
|
||||
Socket socket = new Socket("google.com", 80);
|
||||
|
||||
String payload = "HEAD http://google.com/ HTTP/1.1\r\nHost: google.com\r\nUser-Agent: tcpshield/1.0\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n";
|
||||
socket.getOutputStream().write(payload.getBytes());
|
||||
|
||||
long readTime = System.currentTimeMillis(); // assuming server -> client time is negligible
|
||||
|
||||
List<String> response = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Date serverDate = parseDate(response);
|
||||
|
||||
htpDateOffset = Math.round((serverDate.getTime() - readTime) / 1000.0) * 1000; // the HTTP protocol only returns time in seconds; round offset
|
||||
}
|
||||
|
||||
private Date parseDate(List<String> response) {
|
||||
for (String line : response) {
|
||||
if (!line.startsWith("Date: "))
|
||||
continue;
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
|
||||
try {
|
||||
return sdf.parse(line.substring("Date: ".length()));
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("no date line found - response: " + response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUnixTime() {
|
||||
return (System.currentTimeMillis() + htpDateOffset) / 1000;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package net.tcpshield.tcpshield.validation;
|
||||
|
||||
public interface ITimestampValidator {
|
||||
|
||||
boolean validateTimestamp(long checkedTimestamp);
|
||||
|
||||
default long getTime() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package net.tcpshield.tcpshield.validation;
|
||||
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
import net.tcpshield.tcpshield.validation.impl.DisabledTimestampValidatorImpl;
|
||||
import net.tcpshield.tcpshield.validation.impl.HTPDateTimestampValidatorImpl;
|
||||
import net.tcpshield.tcpshield.validation.impl.SystemTimeTimestampValidatorImpl;
|
||||
|
||||
public class TimestampValidation {
|
||||
|
||||
private final ITimestampValidator timestampValidator;
|
||||
|
||||
public TimestampValidation(TCPShieldConfig config) {
|
||||
switch (config.getTimestampValidationMode().toLowerCase()) {
|
||||
case "system":
|
||||
this.timestampValidator = new SystemTimeTimestampValidatorImpl();
|
||||
break;
|
||||
case "off":
|
||||
this.timestampValidator = new DisabledTimestampValidatorImpl();
|
||||
break;
|
||||
case "htpdate":
|
||||
default:
|
||||
this.timestampValidator = new HTPDateTimestampValidatorImpl();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkTimestamp(long timestamp) {
|
||||
return timestampValidator.validateTimestamp(timestamp);
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return timestampValidator.getTime();
|
||||
}
|
||||
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package net.tcpshield.tcpshield.validation.impl;
|
||||
|
||||
import net.tcpshield.tcpshield.validation.ITimestampValidator;
|
||||
|
||||
/**
|
||||
* @author Fuzzlemann
|
||||
*/
|
||||
public class DisabledTimestampValidatorImpl implements ITimestampValidator {
|
||||
@Override
|
||||
public boolean validateTimestamp(long checkedTimestamp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package net.tcpshield.tcpshield.validation.impl;
|
||||
|
||||
import net.tcpshield.tcpshield.Constants;
|
||||
import net.tcpshield.tcpshield.validation.ITimestampValidator;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* inspired by https://github.com/iridium77/htpdate/blob/master/htpdate.c
|
||||
*/
|
||||
public class HTPDateTimestampValidatorImpl implements ITimestampValidator {
|
||||
|
||||
private volatile long timestampOffset = 0;
|
||||
|
||||
public HTPDateTimestampValidatorImpl() {
|
||||
ForkJoinPool.commonPool().submit(() -> {
|
||||
try {
|
||||
syncTimestamp();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}); // do not execute network requests on the main thread
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateTimestamp(long checkedTimestamp) {
|
||||
long currentTime = getTime();
|
||||
|
||||
return checkedTimestamp >= (currentTime - Constants.MAX_TIME_DIFFERENCE) && checkedTimestamp <= (currentTime + Constants.MAX_TIME_DIFFERENCE);
|
||||
}
|
||||
|
||||
private void syncTimestamp() throws IOException {
|
||||
try (Socket socket = new Socket("google.com", 80)) {
|
||||
// no-cache forces the server to return time
|
||||
String payload = "HEAD http://google.com/ HTTP/1.1\r\nHost: google.com\r\nUser-Agent: tcpshield/1.0\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n";
|
||||
socket.getOutputStream().write(payload.getBytes());
|
||||
|
||||
long readTime = System.currentTimeMillis(); // assuming server -> client time is negligible
|
||||
|
||||
List<String> response = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Date serverDate = extractDate(response);
|
||||
|
||||
timestampOffset = Math.round((serverDate.getTime() - readTime) / 1000.0) * 1000; // the HTTP protocol only returns time in seconds; round offset
|
||||
}
|
||||
}
|
||||
|
||||
private Date extractDate(List<String> response) {
|
||||
for (String line : response) {
|
||||
if (!line.startsWith("Date: ")) continue;
|
||||
|
||||
String dateString = line.substring("Date: ".length());
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
|
||||
try {
|
||||
return sdf.parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("no date line found - response: " + response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTime() {
|
||||
return (System.currentTimeMillis() + timestampOffset) / 1000;
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package net.tcpshield.tcpshield.validation.impl;
|
||||
|
||||
import net.tcpshield.tcpshield.Constants;
|
||||
import net.tcpshield.tcpshield.validation.ITimestampValidator;
|
||||
|
||||
public class SystemTimeTimestampValidatorImpl implements ITimestampValidator {
|
||||
|
||||
@Override
|
||||
public boolean validateTimestamp(long checkedTimestamp) {
|
||||
long currentTime = getTime();
|
||||
|
||||
return checkedTimestamp >= (currentTime - Constants.MAX_TIME_DIFFERENCE) && checkedTimestamp <= (currentTime + Constants.MAX_TIME_DIFFERENCE);
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,81 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import net.tcpshield.tcpshield.TCPShieldPacketHandler;
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.geyser.GeyserUtils;
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
import net.tcpshield.tcpshield.util.Debugger;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import net.tcpshield.tcpshield.velocity.handler.VelocityHandshakeHandler;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@Plugin(id = "tcpshield",
|
||||
name = "TCPShield",
|
||||
description = "TCPShield IP parsing capabilities for Velocity"
|
||||
/**
|
||||
* The entry point for Velocity servers
|
||||
*/
|
||||
@Plugin(
|
||||
id = "tcpshield",
|
||||
name = "TCPShield",
|
||||
description = "TCPShield IP parsing capabilities for Velocity"
|
||||
)
|
||||
public class TCPShieldVelocity {
|
||||
public class TCPShieldVelocity implements TCPShieldPlugin {
|
||||
|
||||
private final ProxyServer server;
|
||||
private final Logger logger;
|
||||
private final Path dataFolder;
|
||||
private final ProxyServer server;
|
||||
private final Logger logger;
|
||||
private final Path dataFolder;
|
||||
|
||||
@Inject
|
||||
public TCPShieldVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataFolder) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
private ConfigProvider configProvider;
|
||||
private TCPShieldPacketHandler packetHandler;
|
||||
private Debugger debugger;
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent e) {
|
||||
server.getEventManager().register(this, new VelocityHandshakePacketHandler(logger, dataFolder.toFile()));
|
||||
}
|
||||
}
|
||||
@Inject
|
||||
public TCPShieldVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataFolder) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent e) {
|
||||
try {
|
||||
configProvider = new VelocityConfig(dataFolder.toFile(), this);
|
||||
debugger = Debugger.createDebugger(this);
|
||||
packetHandler = new TCPShieldPacketHandler(this);
|
||||
|
||||
server.getEventManager().register(this, new VelocityHandshakeHandler(this));
|
||||
|
||||
GeyserUtils.initGeyser(this, configProvider);
|
||||
|
||||
initialization();
|
||||
} catch (Exception exception) {
|
||||
throw new InitializationException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The provider's base methods
|
||||
*/
|
||||
|
||||
@Override
|
||||
public ConfigProvider getConfigProvider() {
|
||||
return configProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TCPShieldPacketHandler getPacketHandler() {
|
||||
return packetHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Debugger getDebugger() {
|
||||
return debugger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package net.tcpshield.tcpshield.velocity;
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.provider.ConfigProvider;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigLoadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigReloadException;
|
||||
import net.tcpshield.tcpshield.util.exception.config.ConfigResetException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ConfigException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* Velocity's configuration implementation
|
||||
*/
|
||||
public class VelocityConfig extends ConfigProvider {
|
||||
|
||||
private final TCPShieldPlugin plugin;
|
||||
|
||||
private Toml loadedToml;
|
||||
|
||||
public VelocityConfig(File dataFolder, TCPShieldPlugin plugin) throws ConfigException {
|
||||
this.plugin = plugin;
|
||||
this.dataFolder = dataFolder;
|
||||
this.configFile = new File(this.dataFolder, "config.toml");
|
||||
|
||||
try {
|
||||
reload();
|
||||
} catch (Exception e) {
|
||||
throw new ConfigException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkNodes(String... nodes) {
|
||||
for (String node : nodes) {
|
||||
if (!loadedToml.contains(node))
|
||||
throw new ConfigException("The node \"" + node + "\" does not exist in the config.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() throws ConfigResetException {
|
||||
try {
|
||||
loadedToml = null; // To the garbage collector you go
|
||||
|
||||
try {
|
||||
configFile.delete();
|
||||
} catch (Exception ignored) {
|
||||
// Just ignore since it either does not exist, or we can overwrite
|
||||
}
|
||||
|
||||
try (InputStream in = VelocityConfig.class.getResourceAsStream("/config.toml")) {
|
||||
Files.copy(in, configFile.toPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ConfigResetException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() throws ConfigLoadException {
|
||||
try {
|
||||
loadedToml = new Toml().read(configFile);
|
||||
|
||||
checkNodes("only-allow-proxy-connections", "timestamp-validation", "debug-mode", "enable-geyser-support", "pre-login-event");
|
||||
|
||||
this.onlyProxy = loadedToml.getBoolean("only-allow-proxy-connections");
|
||||
this.timestampValidationMode = loadedToml.getString("timestamp-validation");
|
||||
this.doDebug = loadedToml.getBoolean("debug-mode");
|
||||
this.geyser = loadedToml.getBoolean("enable-geyser-support");
|
||||
this.velocityHandlePreLoginEvent = loadedToml.getBoolean("pre-login-event");
|
||||
} catch (Exception e) {
|
||||
throw new ConfigLoadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws ConfigReloadException {
|
||||
try {
|
||||
if (!dataFolder.exists())
|
||||
dataFolder.mkdir();
|
||||
|
||||
if (!configFile.exists())
|
||||
reset();
|
||||
|
||||
try {
|
||||
load();
|
||||
} catch (ConfigLoadException exception) {
|
||||
plugin.getLogger().warning("Config loading failed, resetting to default config. (This can be ignored if you just switched builds of TCPShield)");
|
||||
reset();
|
||||
reload(); // Redo cycle, possible StackOverFlow, but realistically only happens if reset fails
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ConfigReloadException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package net.tcpshield.tcpshield.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.ConnectionHandshakeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.HandshakePacketHandler;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityConfigImpl;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityPacketImpl;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityPlayerImpl;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class VelocityHandshakePacketHandler {
|
||||
|
||||
private final HandshakePacketHandler handshakePacketHandler;
|
||||
|
||||
public VelocityHandshakePacketHandler(Logger logger, File dataFolder) {
|
||||
this.handshakePacketHandler = new HandshakePacketHandler(logger, new VelocityConfigImpl(dataFolder));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onHandshake(ConnectionHandshakeEvent e) {
|
||||
InboundConnection connection = e.getConnection();
|
||||
handleEvent(connection);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyPing(ProxyPingEvent e) {
|
||||
InboundConnection connection = e.getConnection();
|
||||
handleEvent(connection);
|
||||
}
|
||||
|
||||
private void handleEvent(InboundConnection connection) {
|
||||
VelocityPlayerImpl player = new VelocityPlayerImpl(connection);
|
||||
if (player.isLegacy()) {
|
||||
player.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
IPacket packet = new VelocityPacketImpl(connection);
|
||||
|
||||
handshakePacketHandler.onHandshake(packet, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package net.tcpshield.tcpshield.velocity.handler;
|
||||
|
||||
import com.velocitypowered.api.event.PostOrder;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.ConnectionHandshakeEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.TCPShieldPlugin;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
|
||||
/**
|
||||
* The handshake handler for Velocity
|
||||
*/
|
||||
public class VelocityHandshakeHandler {
|
||||
|
||||
private final TCPShieldPlugin plugin;
|
||||
|
||||
|
||||
private static Class<?> CONNECTED_PLAYER_CONNECTION_CLASS;
|
||||
|
||||
static {
|
||||
try {
|
||||
CONNECTED_PLAYER_CONNECTION_CLASS = Class.forName("com.velocitypowered.proxy.connection.client.ConnectedPlayer");
|
||||
} catch (Exception e) {
|
||||
// ignore for old velocity versions
|
||||
}
|
||||
}
|
||||
|
||||
public VelocityHandshakeHandler(TCPShieldPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
// Turns out this event sometimes passes erroneous hostnames
|
||||
// which have the null bytes terminated as FML data which causes
|
||||
// issues with the verification process.
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onPreLogin(PreLoginEvent e) {
|
||||
if (!this.plugin.getConfigProvider().handlePreLoginEvent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InboundConnection connection = e.getConnection();
|
||||
handleEvent(connection, "onPreLogin");
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onHandshake(ConnectionHandshakeEvent e) {
|
||||
InboundConnection connection = e.getConnection();
|
||||
handleEvent(connection, "onHandshake");
|
||||
}
|
||||
|
||||
@Subscribe(order = PostOrder.FIRST)
|
||||
public void onProxyPing(ProxyPingEvent e) {
|
||||
InboundConnection connection = e.getConnection();
|
||||
|
||||
if (connection.getClass() == CONNECTED_PLAYER_CONNECTION_CLASS) {
|
||||
// new ServerData (0x42) packet on connect, we don't care about it
|
||||
return;
|
||||
}
|
||||
|
||||
handleEvent(connection, "onProxyPing");
|
||||
}
|
||||
|
||||
private void handleEvent(InboundConnection connection, String debugSource) {
|
||||
VelocityPlayer player = new VelocityPlayer(connection);
|
||||
if (player.getConnectionType() == VelocityPlayer.ConnectionType.LEGACY) {
|
||||
player.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
VelocityPacket packet = new VelocityPacket(connection);
|
||||
|
||||
this.plugin.getDebugger().warn("Velocity: " + debugSource + " Raw player hostname: " + packet.getPayloadString());
|
||||
|
||||
try {
|
||||
plugin.getPacketHandler().handleHandshake(packet, player);
|
||||
} catch (HandshakeException exception) {
|
||||
plugin.getDebugger().exception(exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package net.tcpshield.tcpshield.velocity.handler;
|
||||
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.provider.PacketProvider;
|
||||
import net.tcpshield.tcpshield.util.ReflectionUtil;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PacketManipulationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.HandshakeException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ReflectionException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* A packet wrapper for Velocity
|
||||
*/
|
||||
public class VelocityPacket implements PacketProvider {
|
||||
|
||||
/*
|
||||
* Reflection objects needed for manipulation
|
||||
*/
|
||||
private static final Field HANDSHAKE_FIELD;
|
||||
private static final Field HOSTNAME_FIELD;
|
||||
private static final Field CLEANED_ADDRESS_FIELD;
|
||||
|
||||
// new velocity support
|
||||
private static Class<?> LOGIN_INBOUND_CONNECTION_CLASS;
|
||||
private static Field LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> inboundConnection = Class.forName("com.velocitypowered.proxy.connection.client.InitialInboundConnection");
|
||||
|
||||
HANDSHAKE_FIELD = ReflectionUtil.getPrivateField(inboundConnection, "handshake");
|
||||
|
||||
Field hostnameField;
|
||||
try {
|
||||
hostnameField = ReflectionUtil.getPrivateField(Class.forName("com.velocitypowered.proxy.protocol.packet.HandshakePacket"), "serverAddress");
|
||||
} catch (Exception e) {
|
||||
hostnameField = ReflectionUtil.getPrivateField(Class.forName("com.velocitypowered.proxy.protocol.packet.Handshake"), "serverAddress");
|
||||
}
|
||||
HOSTNAME_FIELD = hostnameField;
|
||||
|
||||
CLEANED_ADDRESS_FIELD = ReflectionUtil.getPrivateField(inboundConnection, "cleanedAddress");
|
||||
} catch (Exception e) {
|
||||
throw new InitializationException(new ReflectionException(e));
|
||||
}
|
||||
|
||||
// LoginInboundConnection support
|
||||
try {
|
||||
LOGIN_INBOUND_CONNECTION_CLASS = Class.forName("com.velocitypowered.proxy.connection.client.LoginInboundConnection");
|
||||
LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD = ReflectionUtil.getPrivateField(LOGIN_INBOUND_CONNECTION_CLASS, "delegate");
|
||||
} catch (Exception e) {
|
||||
// ignore for old versions of velocity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final InboundConnection inboundConnection;
|
||||
private final String rawPayload;
|
||||
|
||||
public VelocityPacket(InboundConnection inboundConnection) {
|
||||
// support new velocity connection type
|
||||
if (inboundConnection.getClass() == LOGIN_INBOUND_CONNECTION_CLASS) {
|
||||
try {
|
||||
inboundConnection = (InboundConnection) LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD.get(inboundConnection);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
this.inboundConnection = inboundConnection;
|
||||
try {
|
||||
this.rawPayload = (String) HOSTNAME_FIELD.get(HANDSHAKE_FIELD.get(inboundConnection));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new PacketManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getPayloadString() {
|
||||
return rawPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPacketHostname(String hostname) throws PacketManipulationException {
|
||||
try {
|
||||
ReflectionUtil.setFinalField(inboundConnection, CLEANED_ADDRESS_FIELD, cleanAddress(hostname));
|
||||
|
||||
Object handshake = HANDSHAKE_FIELD.get(inboundConnection);
|
||||
HOSTNAME_FIELD.set(handshake, hostname);
|
||||
} catch (Exception e) {
|
||||
throw new PacketManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from https://github.com/VelocityPowered/Velocity/blob/17e6944daea8130e03903ccdfbf63f111c573849/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java
|
||||
*/
|
||||
private String cleanAddress(String hostname) {
|
||||
// Clean out any anything after any zero bytes (this includes BungeeCord forwarding and the
|
||||
// legacy Forge handshake indicator).
|
||||
String cleaned = hostname;
|
||||
int zeroIdx = cleaned.indexOf('\0');
|
||||
if (zeroIdx > -1) {
|
||||
cleaned = hostname.substring(0, zeroIdx);
|
||||
}
|
||||
|
||||
// If we connect through an SRV record, there will be a period at the end (DNS usually elides
|
||||
// this ending octet).
|
||||
if (!cleaned.isEmpty() && cleaned.charAt(cleaned.length() - 1) == '.') {
|
||||
cleaned = cleaned.substring(0, cleaned.length() - 1);
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package net.tcpshield.tcpshield.velocity.handler;
|
||||
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.provider.PlayerProvider;
|
||||
import net.tcpshield.tcpshield.util.ReflectionUtil;
|
||||
import net.tcpshield.tcpshield.util.exception.manipulate.PlayerManipulationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.InitializationException;
|
||||
import net.tcpshield.tcpshield.util.exception.phase.ReflectionException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* A player wrapper for Velocity
|
||||
*/
|
||||
public class VelocityPlayer implements PlayerProvider {
|
||||
|
||||
/*
|
||||
* Reflection objects needed for manipulation
|
||||
*/
|
||||
private static final Class<?> INITIAL_INBOUND_CONNECTION_CLASS;
|
||||
private static final Field MINECRAFT_CONNECTION_FIELD;
|
||||
private static final Field LEGACY_MINECRAFT_CONNECTION_FIELD;
|
||||
private static final Field REMOTE_ADDRESS_FIELD;
|
||||
private static final Method CLOSE_CHANNEL_METHOD;
|
||||
|
||||
// new velocity support
|
||||
|
||||
private static Class<?> LOGIN_INBOUND_CONNECTION_CLASS;
|
||||
private static Field LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD;
|
||||
|
||||
static {
|
||||
try {
|
||||
INITIAL_INBOUND_CONNECTION_CLASS = Class.forName("com.velocitypowered.proxy.connection.client.InitialInboundConnection");
|
||||
MINECRAFT_CONNECTION_FIELD = ReflectionUtil.getPrivateField(INITIAL_INBOUND_CONNECTION_CLASS, "connection");
|
||||
LEGACY_MINECRAFT_CONNECTION_FIELD = ReflectionUtil.getPrivateField(Class.forName("com.velocitypowered.proxy.connection.client.HandshakeSessionHandler$LegacyInboundConnection"), "connection");
|
||||
|
||||
Class<?> minecraftConnection = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection");
|
||||
REMOTE_ADDRESS_FIELD = ReflectionUtil.getPrivateField(minecraftConnection, "remoteAddress");
|
||||
CLOSE_CHANNEL_METHOD = minecraftConnection.getMethod("close");
|
||||
} catch (Exception e) {
|
||||
throw new InitializationException(new ReflectionException(e));
|
||||
}
|
||||
|
||||
// LoginInboundConnection support
|
||||
try {
|
||||
LOGIN_INBOUND_CONNECTION_CLASS = Class.forName("com.velocitypowered.proxy.connection.client.LoginInboundConnection");
|
||||
LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD = ReflectionUtil.getPrivateField(LOGIN_INBOUND_CONNECTION_CLASS, "delegate");
|
||||
} catch (Exception e) {
|
||||
// ignore for old versions of velocity
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final InboundConnection inboundConnection;
|
||||
// private final boolean legacy;
|
||||
private final ConnectionType connectionType;
|
||||
private String ip;
|
||||
|
||||
public VelocityPlayer(InboundConnection inboundConnection) {
|
||||
this.inboundConnection = inboundConnection;
|
||||
// this.legacy = inboundConnection.getClass() != INITIAL_INBOUND_CONNECTION_CLASS && inboundConnection.getClass() != LOGIN_INBOUND_CONNECTION_CLASS;
|
||||
this.ip = inboundConnection.getRemoteAddress().getAddress().getHostAddress();
|
||||
|
||||
if (this.inboundConnection.getClass() == INITIAL_INBOUND_CONNECTION_CLASS) {
|
||||
this.connectionType = ConnectionType.INITIAL_INBOUND;
|
||||
} else if (this.inboundConnection.getClass() == LOGIN_INBOUND_CONNECTION_CLASS) {
|
||||
this.connectionType = ConnectionType.LOGIN_INBOUND;
|
||||
} else {
|
||||
this.connectionType = ConnectionType.LEGACY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported with Velocity handshakes
|
||||
*
|
||||
* @return unknown
|
||||
*/
|
||||
@Override
|
||||
public String getUUID() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported with Velocity handshakes
|
||||
*
|
||||
* @return unknown
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws PlayerManipulationException {
|
||||
try {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
|
||||
Object minecraftConnection = this.getMinecraftConnection();
|
||||
REMOTE_ADDRESS_FIELD.set(minecraftConnection, ip);
|
||||
} catch (Exception e) {
|
||||
throw new PlayerManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// public boolean isLegacy() {
|
||||
// return legacy;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
Object minecraftConnection = this.getMinecraftConnection();
|
||||
|
||||
CLOSE_CHANNEL_METHOD.invoke(minecraftConnection);
|
||||
} catch (Exception e) {
|
||||
throw new PlayerManipulationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getMinecraftConnection() {
|
||||
try {
|
||||
switch (this.connectionType) {
|
||||
case LEGACY:
|
||||
return LEGACY_MINECRAFT_CONNECTION_FIELD.get(inboundConnection);
|
||||
case INITIAL_INBOUND:
|
||||
return MINECRAFT_CONNECTION_FIELD.get(inboundConnection);
|
||||
case LOGIN_INBOUND: {
|
||||
// starts as login_inbound, get delegate initial_inbound
|
||||
Object initialInboundConnection = LOGIN_INBOUND_CONNECTION_DELEGATE_FIELD.get(this.inboundConnection);
|
||||
return MINECRAFT_CONNECTION_FIELD.get(initialInboundConnection);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ConnectionType getConnectionType() {
|
||||
return connectionType;
|
||||
}
|
||||
|
||||
enum ConnectionType {
|
||||
LOGIN_INBOUND, INITIAL_INBOUND, LEGACY
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package net.tcpshield.tcpshield.velocity.impl;
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class VelocityConfigImpl extends TCPShieldConfig {
|
||||
|
||||
private final File dataFolder;
|
||||
private final File file;
|
||||
|
||||
public VelocityConfigImpl(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
this.file = new File(this.dataFolder, "config.toml");
|
||||
|
||||
saveDefaultConfig();
|
||||
Toml toml = loadConfig();
|
||||
this.onlyProxy = toml.getBoolean("only-allow-proxy-connections");
|
||||
this.timestampValidationMode = toml.getString("timestamp-validation");
|
||||
this.debug = toml.getBoolean("debug-mode");
|
||||
}
|
||||
|
||||
private void saveDefaultConfig() {
|
||||
if (!dataFolder.exists()) dataFolder.mkdir();
|
||||
if (file.exists()) return;
|
||||
|
||||
try (InputStream in = VelocityConfigImpl.class.getResourceAsStream("/config.toml")) {
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private File getConfigFile() {
|
||||
return new File(dataFolder, "config.toml");
|
||||
}
|
||||
|
||||
private Toml loadConfig() {
|
||||
return new Toml().read(getConfigFile());
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package net.tcpshield.tcpshield.velocity.impl;
|
||||
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.exception.TCPShieldInitializationException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class VelocityPacketImpl implements IPacket {
|
||||
|
||||
private static final Field HANDSHAKE_FIELD;
|
||||
private static final Field HOSTNAME_FIELD;
|
||||
private static final Field CLEANED_ADDRESS_FIELD;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> inboundConnection = Class.forName("com.velocitypowered.proxy.connection.client.InitialInboundConnection");
|
||||
|
||||
HANDSHAKE_FIELD = ReflectionUtils.getPrivateField(inboundConnection, "handshake");
|
||||
HOSTNAME_FIELD = ReflectionUtils.getPrivateField(Class.forName("com.velocitypowered.proxy.protocol.packet.Handshake"), "serverAddress");
|
||||
CLEANED_ADDRESS_FIELD = ReflectionUtils.getPrivateField(inboundConnection, "cleanedAddress");
|
||||
} catch (NoSuchFieldException | ClassNotFoundException e) {
|
||||
throw new TCPShieldInitializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final InboundConnection inboundConnection;
|
||||
private final String rawPayload;
|
||||
|
||||
public VelocityPacketImpl(InboundConnection inboundConnection) {
|
||||
this.inboundConnection = inboundConnection;
|
||||
try {
|
||||
this.rawPayload = (String) HOSTNAME_FIELD.get(HANDSHAKE_FIELD.get(inboundConnection));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e); // pass on
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawPayload() {
|
||||
return rawPayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) throws Exception {
|
||||
ReflectionUtils.setFinalField(inboundConnection, CLEANED_ADDRESS_FIELD, hostname);
|
||||
|
||||
Object handshake = HANDSHAKE_FIELD.get(inboundConnection);
|
||||
HOSTNAME_FIELD.set(handshake, hostname);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package net.tcpshield.tcpshield.velocity.impl;
|
||||
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.tcpshield.tcpshield.ReflectionUtils;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.exception.IPModificationFailureException;
|
||||
import net.tcpshield.tcpshield.exception.TCPShieldInitializationException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class VelocityPlayerImpl implements IPlayer {
|
||||
|
||||
private static final Field MINECRAFT_CONNECTION_FIELD;
|
||||
private static final Field LEGACY_MINECRAFT_CONNECTION_FIELD;
|
||||
private static final Field REMOTE_ADDRESS_FIELD;
|
||||
private static final Method CLOSE_CHANNEL_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
MINECRAFT_CONNECTION_FIELD = ReflectionUtils.getPrivateField(Class.forName("com.velocitypowered.proxy.connection.client.InitialInboundConnection"), "connection");
|
||||
LEGACY_MINECRAFT_CONNECTION_FIELD = ReflectionUtils.getPrivateField(Class.forName("com.velocitypowered.proxy.connection.client.HandshakeSessionHandler$LegacyInboundConnection"), "connection");
|
||||
|
||||
Class<?> minecraftConnection = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection");
|
||||
REMOTE_ADDRESS_FIELD = ReflectionUtils.getPrivateField(minecraftConnection, "remoteAddress");
|
||||
CLOSE_CHANNEL_METHOD = minecraftConnection.getMethod("close");
|
||||
} catch (NoSuchFieldException | ClassNotFoundException | NoSuchMethodException e) {
|
||||
throw new TCPShieldInitializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final InboundConnection inboundConnection;
|
||||
private final boolean legacy;
|
||||
private String ip;
|
||||
|
||||
public VelocityPlayerImpl(InboundConnection inboundConnection) {
|
||||
this.inboundConnection = inboundConnection;
|
||||
this.legacy = inboundConnection.getProtocolVersion().isUnknown() || inboundConnection.getProtocolVersion().isLegacy();
|
||||
this.ip = inboundConnection.getRemoteAddress().getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUUID() {
|
||||
return "unknown"; // not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown"; // not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return legacy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws IPModificationFailureException {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
|
||||
try {
|
||||
Object minecraftConnection = MINECRAFT_CONNECTION_FIELD.get(inboundConnection);
|
||||
REMOTE_ADDRESS_FIELD.set(minecraftConnection, ip);
|
||||
} catch (Exception e) {
|
||||
throw new IPModificationFailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
Object minecraftConnection = legacy ? LEGACY_MINECRAFT_CONNECTION_FIELD.get(inboundConnection) : MINECRAFT_CONNECTION_FIELD.get(inboundConnection);
|
||||
|
||||
CLOSE_CHANNEL_METHOD.invoke(minecraftConnection);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e); // pass exception on
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
name: TCPShield
|
||||
version: '2.1'
|
||||
version: 2.8.0
|
||||
main: net.tcpshield.tcpshield.bungee.TCPShieldBungee
|
||||
author: https://tcpshield.com
|
||||
softdepends:
|
||||
- floodgate
|
||||
@@ -5,4 +5,10 @@ only-allow-proxy-connections = true
|
||||
timestamp-validation = 'htpdate'
|
||||
|
||||
# Turn on to diagnose connection issues
|
||||
debug-mode = false
|
||||
debug-mode = false
|
||||
|
||||
# Toggle to enable support for Geyser/Floodgate v2
|
||||
enable-geyser-support = false
|
||||
|
||||
# enable handling handshake on PreLoginEvent
|
||||
pre-login-event = true
|
||||
@@ -5,4 +5,10 @@ only-allow-proxy-connections: true
|
||||
timestamp-validation: 'htpdate'
|
||||
|
||||
# Turn on to diagnose connection issues
|
||||
debug-mode: false
|
||||
debug-mode: false
|
||||
|
||||
# Toggle to enable support for Geyser/Floodgate v2
|
||||
enable-geyser-support: false
|
||||
|
||||
# Spigot/Paper option only, does not affect bungeecord
|
||||
prefer-protocollib: false
|
||||
@@ -1,6 +1,9 @@
|
||||
name: TCPShield
|
||||
version: '2.1'
|
||||
version: 2.8.0
|
||||
main: net.tcpshield.tcpshield.bukkit.TCPShieldBukkit
|
||||
softdepend:
|
||||
- ProtocolLib
|
||||
- floodgate
|
||||
author: https://tcpshield.com
|
||||
description: TCPShield IP parsing capabilities for Velocity
|
||||
api-version: 1.16
|
||||
@@ -1,11 +1 @@
|
||||
{
|
||||
"id": "tcpshield",
|
||||
"name": "TCPShield",
|
||||
"version": "2.0",
|
||||
"description": "TCPShield IP parsing capabilities for Velocity",
|
||||
"authors": [
|
||||
"TCPShield"
|
||||
],
|
||||
"dependencies": [],
|
||||
"main": "net.tcpshield.tcpshield.velocity.TCPShieldVelocity"
|
||||
}
|
||||
{"id":"tcpshield","name":"TCPShield","version":"2.8.0","description":"TCPShield IP parsing capabilities for Velocity","authors":["TCPShield"],"dependencies":[{"id":"floodgate","optional":true}],"main":"net.tcpshield.tcpshield.velocity.TCPShieldVelocity"}
|
||||
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user