Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6216b0c01e | ||
|
|
901ce0cfeb | ||
|
|
a537099297 | ||
|
|
7a14a45338 | ||
|
|
8f35cf9532 | ||
|
|
39a977fa97 | ||
|
|
2178727b21 | ||
|
|
553332c641 | ||
|
|
23c207e3b9 | ||
|
|
bf40833d5d | ||
|
|
818df3cbb1 | ||
|
|
fcc3ef4fef | ||
|
|
d39f520279 |
No files matched your search
@@ -22,4 +22,11 @@ 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://docs.tcpshield.com/about-us)
|
||||
|
||||
### Contributors
|
||||
|
||||
These wonderful contributors have helped TCPShield make this plugin better!
|
||||
|
||||
* [Fuzzlemann](https://github.com/Fuzzlemann)
|
||||
* [RyanDeLap](https://github.com/RyanDeLap)
|
||||
+1
-1
@@ -65,7 +65,7 @@ configurations {
|
||||
testImplementation.extendsFrom compileOnly
|
||||
}
|
||||
|
||||
version = '2.0'
|
||||
version = '2.2'
|
||||
group = 'net.tcpshield.tcpshield'
|
||||
archivesBaseName = 'TCPShield'
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ public class HandshakePacketHandler {
|
||||
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.", player.getName(), player.getUUID(), player.getIP(), timestamp, currentTime));
|
||||
"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()) {
|
||||
|
||||
@@ -2,24 +2,47 @@ package net.tcpshield.tcpshield;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import net.tcpshield.tcpshield.exception.TCPShieldInitializationException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
private static final Table<Class<?>, String, Field> CACHED_FIELDS = HashBasedTable.create();
|
||||
private static final Field MODIFIERS_FIELD;
|
||||
|
||||
static {
|
||||
Field tempModifiersField;
|
||||
try {
|
||||
tempModifiersField = getDeclaredField(Field.class, "modifiers");
|
||||
} catch (NoSuchFieldException 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);
|
||||
tempModifiersField = Arrays.stream(fields).filter(field -> field.getName().equals("modifiers")).findFirst().orElseThrow(() -> new TCPShieldInitializationException("Could not find the modifiers field"));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e2) {
|
||||
throw new TCPShieldInitializationException(e2);
|
||||
}
|
||||
}
|
||||
|
||||
MODIFIERS_FIELD = tempModifiersField;
|
||||
MODIFIERS_FIELD.setAccessible(true);
|
||||
}
|
||||
|
||||
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 {
|
||||
public static void setFinalField(Object object, Field field, Object value) throws IllegalAccessException {
|
||||
field.setAccessible(true);
|
||||
if (Modifier.isFinal(field.getModifiers())) {
|
||||
Field modifiersField = getDeclaredField(Field.class, "modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
MODIFIERS_FIELD.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
}
|
||||
|
||||
setField(object, field, value);
|
||||
@@ -58,7 +81,18 @@ public class ReflectionUtils {
|
||||
Field cachedField = CACHED_FIELDS.get(clazz, fieldName);
|
||||
if (cachedField != null) return cachedField;
|
||||
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ public class TCPShieldBukkit extends JavaPlugin {
|
||||
}
|
||||
|
||||
private boolean isPaper() {
|
||||
if (true) return false; // disabled for now
|
||||
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.event.player.PlayerHandshakeEvent");
|
||||
return true;
|
||||
|
||||
@@ -39,6 +39,7 @@ public class PaperPlayerImpl implements IPlayer {
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
handshakeEvent.setCancelled(false);
|
||||
handshakeEvent.setFailMessage("Connection failed. Please try again or contract an administrator.");
|
||||
handshakeEvent.setFailed(true);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
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 {
|
||||
|
||||
@@ -15,22 +10,5 @@ public class TCPShieldBungee extends Plugin implements Listener {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,8 @@ public class TCPShieldInitializationException extends RuntimeException {
|
||||
public TCPShieldInitializationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public TCPShieldInitializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ 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.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityConfigImpl;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityPacketImpl;
|
||||
import net.tcpshield.tcpshield.velocity.impl.VelocityPlayerImpl;
|
||||
@@ -35,7 +34,12 @@ public class VelocityHandshakePacketHandler {
|
||||
}
|
||||
|
||||
private void handleEvent(InboundConnection connection) {
|
||||
IPlayer player = new VelocityPlayerImpl(connection);
|
||||
VelocityPlayerImpl player = new VelocityPlayerImpl(connection);
|
||||
if (player.isLegacy()) {
|
||||
player.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
IPacket packet = new VelocityPacketImpl(connection);
|
||||
|
||||
handshakePacketHandler.onHandshake(packet, player);
|
||||
|
||||
@@ -11,11 +11,15 @@ 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 {
|
||||
HANDSHAKE_FIELD = ReflectionUtils.getPrivateField(Class.forName("com.velocitypowered.proxy.connection.client.InitialInboundConnection"), "handshake");
|
||||
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);
|
||||
}
|
||||
@@ -40,7 +44,29 @@ public class VelocityPacketImpl implements IPacket {
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) throws Exception {
|
||||
ReflectionUtils.setFinalField(inboundConnection, CLEANED_ADDRESS_FIELD, cleanAddress(hostname));
|
||||
|
||||
Object handshake = HANDSHAKE_FIELD.get(inboundConnection);
|
||||
HOSTNAME_FIELD.set(handshake, hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,14 @@ 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");
|
||||
@@ -29,10 +31,12 @@ public class VelocityPlayerImpl implements IPlayer {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -41,6 +45,7 @@ public class VelocityPlayerImpl implements IPlayer {
|
||||
return "unknown"; // not supported
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unknown"; // not supported
|
||||
}
|
||||
@@ -50,6 +55,10 @@ public class VelocityPlayerImpl implements IPlayer {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public boolean isLegacy() {
|
||||
return legacy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws IPModificationFailureException {
|
||||
this.ip = ip.getAddress().getHostAddress();
|
||||
@@ -65,7 +74,8 @@ public class VelocityPlayerImpl implements IPlayer {
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
Object minecraftConnection = MINECRAFT_CONNECTION_FIELD.get(inboundConnection);
|
||||
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,4 @@
|
||||
name: TCPShield
|
||||
version: '2.0'
|
||||
version: '2.2'
|
||||
main: net.tcpshield.tcpshield.bungee.TCPShieldBungee
|
||||
author: https://tcpshield.com
|
||||
@@ -1,5 +1,5 @@
|
||||
name: TCPShield
|
||||
version: '2.0'
|
||||
version: '2.2'
|
||||
main: net.tcpshield.tcpshield.bukkit.TCPShieldBukkit
|
||||
softdepend:
|
||||
- ProtocolLib
|
||||
|
||||
Reference in new issue
Block a user