Add forced hosts support to transfer feature (#588)

* feat: add use-handshake-hostname config option for forced hosts support

* Solve issues
This commit is contained in:
MISHA authored and GitHub committed 2026-06-03 21:14:43 +02:00
1 parent 104691b816
commit bf29fa5ca7
15 files changed
+105 -19

No files matched your search

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2025 Sonar Contributors
* Copyright (C) 2026 Sonar Contributors
*
* 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
@@ -21,6 +21,7 @@ import io.netty.channel.Channel;
import io.netty.util.ReferenceCountUtil;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
import xyz.jonesdev.sonar.api.timer.SystemTimer;
@@ -45,6 +46,8 @@ public interface SonarUser {
boolean isGeyser();
@Nullable String getHostname();
/**
* Disconnect the player during/after verification
* using our custom Disconnect packet.
@@ -48,6 +48,7 @@ import static xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer.*;
public abstract class InboundHandlerAdapter extends ChannelInboundHandlerAdapter {
protected @Nullable String username;
protected ProtocolVersion protocolVersion;
protected @Nullable String handshakeHostname;
protected @Nullable RemovalListener channelRemovalListener;
/**
@@ -65,6 +66,7 @@ public abstract class InboundHandlerAdapter extends ChannelInboundHandlerAdapter
throw QuietDecoderException.INSTANCE;
}
protocolVersion = ProtocolVersion.fromId(protocol);
handshakeHostname = hostname;
ctx.pipeline().addFirst(SONAR_BANDWIDTH, BandwidthHandler.INSTANCE);
}
@@ -166,7 +168,8 @@ public abstract class InboundHandlerAdapter extends ChannelInboundHandlerAdapter
}
// Create an instance for the user and let the verification handler take over the channel
return () -> new UserWrapper(ctx, inetAddress, protocolVersion, username, fingerprint, geyser);
return () -> new UserWrapper(ctx, inetAddress, protocolVersion, username, fingerprint, geyser,
handshakeHostname);
});
}
@@ -25,6 +25,7 @@ import lombok.ToString;
import lombok.experimental.Accessors;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.jonesdev.sonar.api.Sonar;
import xyz.jonesdev.sonar.api.antibot.SonarUser;
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
@@ -47,7 +48,7 @@ import java.net.InetAddress;
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.*;
@Getter
@ToString(of = {"protocolVersion", "inetAddress", "geyser"})
@ToString(of = {"protocolVersion", "inetAddress", "hostname", "geyser"})
public final class UserWrapper implements SonarUser {
@Accessors(fluent = true)
private final Channel channel;
@@ -56,6 +57,7 @@ public final class UserWrapper implements SonarUser {
private final String fingerprint;
private final String username;
private final boolean geyser;
private final @Nullable String hostname;
private final SystemTimer loginTimer;
@Setter
private boolean forceCaptcha;
@@ -65,13 +67,15 @@ public final class UserWrapper implements SonarUser {
final @NotNull ProtocolVersion protocolVersion,
final @NotNull String username,
final @NotNull String fingerprint,
final boolean geyser) {
final boolean geyser,
final @Nullable String hostname) {
this.channel = ctx.channel();
this.inetAddress = inetAddress;
this.protocolVersion = protocolVersion;
this.username = username;
this.fingerprint = fingerprint;
this.geyser = geyser;
this.hostname = hostname;
this.loginTimer = new SystemTimer();
GlobalSonarStatistics.totalAttemptedVerifications++;
@@ -128,7 +128,6 @@ public class SonarPacketPreparer {
public SonarPacket spawnPosition;
public SonarPacket fallStartPosition;
public SonarPacket fallStartPositionLegacy;
public static SonarPacket transferToOrigin;
public SonarPacket blacklisted;
public SonarPacket alreadyQueued;
public SonarPacket alreadyVerifying;
@@ -215,15 +214,6 @@ public class SonarPacketPreparer {
unsupportedVersion = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getUnsupportedVersion(), true));
tooManyOnlinePerIP = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getTooManyOnlinePerIp(), true));
// Prepare transfer packet
if (Sonar.get0().getConfig().getGeneralConfig().getBoolean("verification.transfer.enabled")) {
transferToOrigin = new SonarPacketSnapshot(new TransferPacket(
Sonar.get0().getConfig().getGeneralConfig().getString("verification.transfer.destination-host"),
Sonar.get0().getConfig().getGeneralConfig().getInt("verification.transfer.destination-port")));
} else {
transferToOrigin = null;
}
// Prepare update time packet
final int timeOfDay = Sonar.get0().getConfig().getVerification().getTimeOfDay();
if (timeOfDay != 1000) {
@@ -31,14 +31,13 @@ import xyz.jonesdev.sonar.common.netty.MinecraftVarIntLengthEncoder;
import xyz.jonesdev.sonar.common.protocol.SonarPacketDecoder;
import xyz.jonesdev.sonar.common.protocol.SonarPacketEncoder;
import xyz.jonesdev.sonar.common.protocol.SonarPacketListener;
import xyz.jonesdev.sonar.common.protocol.packets.play.TransferPacket;
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
import java.util.Random;
import static xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer.transferToOrigin;
@RequiredArgsConstructor
public abstract class VerificationHandler implements SonarPacketListener {
protected final SonarUser user;
@@ -57,17 +56,28 @@ public abstract class VerificationHandler implements SonarPacketListener {
// If enabled, transfer the player back to the origin server.
// This feature was introduced by Mojang in Minecraft version 1.20.5.
if (transferToOrigin != null && user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)
&& Sonar.get0().getConfig().getGeneralConfig().getBoolean("verification.transfer.enabled")) {
// Use the original handshake hostname if configured and available (for forced hosts / proxy setups),
// otherwise fall back to the configured destination host.
final String hostname = user.getHostname();
final boolean useHandshake = Sonar.get0().getConfig().getGeneralConfig()
.getBoolean("verification.transfer.use-handshake-hostname");
final String destinationHost = useHandshake && hostname != null && !hostname.isEmpty()
? hostname
: Sonar.get0().getConfig().getGeneralConfig().getString("verification.transfer.destination-host");
final int destinationPort = Sonar.get0().getConfig().getGeneralConfig().getInt("verification.transfer.destination-port");
final TransferPacket transferPacket = new TransferPacket(destinationHost, destinationPort);
// Send the transfer packet to the player (and close the channel if on Java Edition)
if (user.isGeyser()) {
user.write(transferToOrigin);
user.write(transferPacket);
// Make sure we cannot receive any more packets from the player
user.channel().pipeline().remove(SonarPacketDecoder.class);
user.channel().pipeline().remove(SonarPacketEncoder.class);
user.channel().pipeline().remove(MinecraftVarInt21FrameDecoder.class);
user.channel().pipeline().remove(MinecraftVarIntLengthEncoder.class);
} else {
ProtocolUtil.closeWith(user.channel(), user.getProtocolVersion(), transferToOrigin);
ProtocolUtil.closeWith(user.channel(), user.getProtocolVersion(), transferPacket);
}
} else {
// Disconnect player with the verification success message
@@ -155,12 +155,19 @@ verification:
# TransferTool pro Geyser, aby byli hráči Bedrocku přesměrováni na správný server.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Pokud používáte proxy s forced hosts, Sonar může použít název hostitele,
# se kterým se hráč původně připojil (z handshake paketu), jako cíl přenosu.
# Vypněte to, pokud jsou vaše forced hosts pouze pro interní směrování
# a chcete, aby všichni hráči byli přeneseni na nakonfigurovaný destination-host.
use-handshake-hostname: false
# Na který server by měl Sonar přenést hráče, když projde ověřením?
# Zadejte IP serveru, který hráči používají k běžnému připojení k vašemu serveru.
# Například můžete zadat "mc.hypixel.net" nebo přímou IP jako "1.1.1.1"
destination-host: "play.my-server.com"
# Jaký port by měl Sonar použít při přenosu hráče na původní server?
# Pokud váš server nepotřebuje port pro připojení, můžete toto ponechat jako 25565.
# Pokud používáte forced hosts (use-handshake-hostname: true), nastavte to na port,
# na kterém proxy naslouchá (port "bind" v konfiguraci vašeho proxy).
destination-port: 25565
# Gamemode hráče při verifikaci
@@ -152,12 +152,20 @@ verification:
# TransferTool für Geyser einrichten, damit Bedrock-Spieler auf den richtigen Server umgeleitet werden.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Wenn Sie einen Proxy mit forced hosts verwenden, kann Sonar den Hostnamen,
# mit dem der Spieler ursprünglich verbunden war (aus dem Handshake-Paket),
# als Übertragungsziel verwenden.
# Deaktivieren Sie diese Option, wenn Ihre forced hosts nur für das interne
# Routing sind und alle Spieler zum konfigurierten destination-host übertragen werden sollen.
use-handshake-hostname: false
# Auf welchen Server soll Sonar den Spieler übertragen, wenn die Verifizierung bestanden wurde?
# Bitte geben Sie die Server-IP ein, die von Spielern verwendet wird, um sich zu verbinden.
# Sie können beispielsweise "mc.hypixel.net" oder eine direkte IP wie "1.1.1.1" verwenden.
destination-host: "play.my-server.com"
# Welchen Port sollte Sonar verwenden, wenn der Spieler auf den Ursprungsserver gesendet wird?
# Wenn Ihr Server keinen Port zum Herstellen einer Verbindung benötigt, können Sie diesen auf 25565 belassen.
# Wenn Sie forced hosts verwenden (use-handshake-hostname: true), setzen Sie dies auf
# den Port, auf dem der Proxy horcht (den "Bind"-Port in Ihrer Proxy-Konfiguration).
destination-port: 25565
# Der Spielmodus des Spielers während der Überprüfung
@@ -155,12 +155,19 @@ verification:
# TransferTool for Geyser, so Bedrock players are redirected to the correct server.
# https://github.com/onebeastchris/TransferTool
enabled: false
# When behind a proxy with forced hosts, Sonar can use the hostname the player
# originally connected with (from the handshake packet) as the transfer destination.
# Disable this if your forced hosts are for internal routing only and you want
# all players to be transferred to the configured destination-host instead.
use-handshake-hostname: false
# Which server should Sonar transfer the player to when the verification is passed?
# Please enter the server IP used by players to normally connect to your server.
# For example, you can put "mc.hypixel.net" or a direct IP like "1.1.1.1"
destination-host: "play.my-server.com"
# Which port should Sonar use when transferring the player to the origin server?
# If your server does not need a port to connect to, you can leave this as 25565.
# If you are using forced hosts (use-handshake-hostname: true), set this to
# the port the proxy is listening on (the "bind" port in your proxy config).
destination-port: 25565
# The gamemode of the player during verification
@@ -154,12 +154,20 @@ verification:
# TransferTool pour Geyser, afin que les joueurs Bedrock soient redirigés vers le bon serveur.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Lorsque vous utilisez un proxy avec forced hosts, Sonar peut utiliser le nom d'hôte
# que le joueur a utilisé initialement pour se connecter (du paquet handshake) comme
# destination du transfert.
# Désactivez cette option si vos forced hosts sont uniquement pour le routage interne
# et que vous souhaitez que tous les joueurs soient transférés vers le destination-host configuré.
use-handshake-hostname: false
# Quel serveur Sonar devrait-il utiliser pour transférer le joueur une fois la vérification réussie ?
# Veuillez entrer l'adresse IP du serveur que les joueurs utilisent normalement pour se connecter à votre serveur.
# Par exemple, vous pouvez mettre "mc.hypixel.net" ou une IP directe comme "1.1.1.1"
destination-host: "play.my-server.com"
# Quel port Sonar devrait-il utiliser pour transférer le joueur au serveur d'origine ?
# Si votre serveur n'a pas besoin d'un port pour se connecter, vous pouvez laisser celui-ci comme 25565.
# Si vous utilisez forced hosts (use-handshake-hostname: true), définissez ce port sur
# le port d'écoute du proxy (le port "bind" dans la configuration de votre proxy).
destination-port: 25565
# Le mode de jeu du joueur pendant la vérification
@@ -153,12 +153,20 @@ verification:
# TransferTool Geyser-ისთვის, ასე რომ Bedrock მოთამაშეები გადამისამართდებიან სწორ სერვერზე.
# https://github.com/onebeastchris/TransferTool
enabled: false
# როდესაც იყენებთ პროქსის forced hosts-თან, Sonar-ს შეუძლია გამოიყენოს ჰოსტის სახელი,
# რომლითაც მოთამაშე თავდაპირველად დაუკავშირდა (handshake პაკეტიდან), როგორც
# ტრანსფერის დანიშნულება.
# გამორთეთ ეს, თუ თქვენი forced hosts არის მხოლოდ შიდა მარშრუტიზაციისთვის
# და გსურთ ყველა მოთამაშის გადაყვანა კონფიგურირებულ destination-host-ზე.
use-handshake-hostname: false
# რომელ სერვერზე უნდა გადაიყვანოს Sonar-მა მოთამაშე ვერიფიკაციის გავლის შემდეგ?
# გთხოვთ, შეიყვანოთ სერვერის IP მისამართი, რომლითაც მოთამაშეები ჩვეულებრივ უერთდებიან თქვენს სერვერს
# მაგალითად, შეგიძლიათ ჩაწეროთ "mc.hypixel.net" ან პირდაპირი IP როგორიცაა "1.1.1.1"
destination-host: "play.my-server.com"
# რომელი პორტი უნდა გამოიყენოს Sonar-მა მოთამაშის საწყის სერვერზე გადაყვანისას?
# თუ თქვენს სერვერს არ სჭირდება პორტი შესასვლელად, შეგიძლიათ დატოვოთ ეს 25565-ზე
# თუ იყენებთ forced hosts-ს (use-handshake-hostname: true), დააყენეთ ეს იმ პორტზე,
# რომელზეც პროქსი უსმენს ("bind" პორტი თქვენი პროქსის კონფიგურაციაში).
destination-port: 25565
# მოთამაშის თამაშის რეჟიმი გადამოწმების დროს
@@ -154,12 +154,20 @@ verification:
# TransferTool voor Geyser, zodat Bedrock spelers worden doorgestuurd naar de juiste server.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Wanneer u een proxy met forced hosts gebruikt, kan Sonar de hostnaam gebruiken
# waarmee de speler oorspronkelijk verbinding maakte (uit het handshake-pakket)
# als overdrachtsbestemming.
# Schakel dit uit als uw forced hosts alleen voor interne routering zijn en u
# wilt dat alle spelers worden overgebracht naar de geconfigureerde destination-host.
use-handshake-hostname: false
# Naar welke server moet Sonar de speler overbrengen wanneer de verificatie is geslaagd?
# Voer het server-IP in dat door spelers wordt gebruikt om normaal verbinding te maken met uw server.
# U kunt bijvoorbeeld "mc.hypixel.net" of een direct IP-adres zoals "1.1.1.1" invoeren
destination-host: "play.my-server.com"
# Welke poort moet Sonar gebruiken bij het overbrengen van de speler naar de oorspronkelijke server?
# Als uw server geen poort nodig heeft om verbinding mee te maken, kunt u dit op 25565 laten staan.
# Als u forced hosts gebruikt (use-handshake-hostname: true), stelt u dit in op
# de poort waarop de proxy luistert (de "bind"-poort in uw proxyconfiguratie).
destination-port: 25565
# De spelmodus van de speler tijdens verificatie
@@ -154,12 +154,19 @@ verification:
# TransferTool dla Geyser, aby gracze Bedrock byli przekierowywani na właściwy serwer.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Gdy używasz proxy z forced hosts, Sonar może użyć nazwy hosta, z którą
# gracz pierwotnie się łączył (z pakietu handshake) jako celu transferu.
# Wyłącz tę opcję, jeśli twoje forced hosts są tylko do wewnętrznego routingu
# i chcesz, aby wszyscy gracze byli przenoszeni na skonfigurowany destination-host.
use-handshake-hostname: false
# Na który serwer Sonar powinien przenieść gracza po pomyślnym przejściu weryfikacji?
# Wprowadź adres IP serwera, który jest używany przez graczy do normalnego łączenia się z serwerem.
# Na przykład, możesz wpisać "mc.hypixel.net" lub bezpośredni adres IP, taki jak "1.1.1.1".
destination-host: "play.my-server.com"
# Jaki port powinien używać Sonar podczas przenoszenia gracza do serwera początkowego?
# Jeśli serwer nie potrzebuje portu do połączenia, można pozostawić 25565.
# Jeśli używasz forced hosts (use-handshake-hostname: true), ustaw to na port,
# na którym nasłuchuje proxy (port "bind" w konfiguracji twojego proxy).
destination-port: 25565
# Tryb gry gracza (gamemode) podczas weryfikacji
@@ -154,12 +154,19 @@ verification:
# o TransferTool para Geyser, para que jogadores Bedrock sejam redirecionados para o servidor correto.
# https://github.com/onebeastchris/TransferTool
enabled: false
# Ao usar um proxy com forced hosts, o Sonar pode usar o hostname que o jogador
# usou originalmente para se conectar (do pacote handshake) como destino da transferência.
# Desative esta opção se seus forced hosts forem apenas para roteamento interno
# e você quiser que todos os jogadores sejam transferidos para o destination-host configurado.
use-handshake-hostname: false
# Para qual servidor o Sonar deve transferir o jogador quando a verificação for passada?
# Por favor, insira o IP do servidor usado pelos jogadores para se conectar normalmente ao seu servidor.
# Por exemplo, você pode colocar "mc.hypixel.net" ou um IP direto como "1.1.1.1"
destination-host: "play.my-server.com"
# Qual porta o Sonar deve usar ao transferir o jogador para o servidor de origem?
# Se seu servidor não precisar de uma porta para se conectar, você pode deixar isso como 25565.
# Se você estiver usando forced hosts (use-handshake-hostname: true), defina isto para
# a porta em que o proxy está ouvindo (a porta "bind" na configuração do seu proxy).
destination-port: 25565
# O modo de jogo do jogador durante a verificação
@@ -153,12 +153,21 @@ verification:
# TransferTool для Geyser, чтобы игроки Bedrock перенаправлялись на правильный сервер.
# https://github.com/onebeastchris/TransferTool
enabled: false
# При использовании прокси с forced hosts Sonar может использовать имя хоста,
# с которого игрок изначально подключился (из handshake-пакета), в качестве
# адреса назначения при передаче.
# Отключите эту опцию, если ваши forced hosts используются только для внутренней
# маршрутизации и вы хотите, чтобы все игроки перенаправлялись на настроенный
# destination-host.
use-handshake-hostname: false
# На какой сервер должен Sonar переместить игрока после прохождения проверки?
# Введите IP-адрес сервера, который обычно используется игроками для подключения к вашему серверу.
# Например, вы можете указать "mc.hypixel.net" или прямой IP-адрес, например "1.1.1.1"
destination-host: "play.my-server.com"
# Какой порт должен использовать Sonar при перемещении игрока на исходный сервер?
# Если ваш сервер не требует порта для подключения, вы можете оставить это как 25565.
# Если вы используете forced hosts (use-handshake-hostname: true), укажите порт,
# на котором слушает прокси (порт "bind" в конфигурации вашего прокси).
destination-port: 25565
# Режим игрока во время проверки
@@ -150,12 +150,19 @@ verification:
# TransferTool 扩展, 以便将 Bedrock 玩家重定向到正确的服务器.
# https://github.com/onebeastchris/TransferTool
enabled: false
# 当代理处于 forced hosts 模式时, Sonar 可以使用玩家最初连接时使用的
# 主机名(来自握手包)作为传输目标地址。
# 如果您的 forced hosts 仅用于内部路由, 并且您希望所有玩家都传输到
# 配置的 destination-host, 请禁用此选项。
use-handshake-hostname: false
# 当玩家通过验证后, Sonar 应该将玩家转移到哪个服务器
# 请输入玩家正常连接服务器时使用的服务器IP或域名
# 例如使用域名("mc.hypixel.net") 或使用 IP 地址("1.1.1.1").
destination-host: "play.my-server.com"
# Sonar 应该告诉玩家通过以上域名的什么端口来连接到服务器?
# 如果服务器地址不包含端口 请保留25565.
# 如果您使用 forced hosts (use-handshake-hostname: true), 请将此设置为
# 代理服务器监听的端口 (代理配置中的 "bind" 端口)。
destination-port: 25565
# 玩家正在验证时所使用的游戏模式