2020-05-23 21:50:04 +00:00
/ *
2022-01-01 19:03:05 +00:00
* Copyright ( c ) 2019 - 2022 GeyserMC . http : //geysermc.org
2020-05-23 21:50:04 +00:00
*
2020-07-30 20:10:15 +00:00
* 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 :
2020-05-23 21:50:04 +00:00
*
2020-07-30 20:10:15 +00:00
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
2020-05-23 21:50:04 +00:00
*
2020-07-30 20:10:15 +00:00
* 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 .
2020-05-23 21:50:04 +00:00
*
2020-07-30 20:10:15 +00:00
* @author GeyserMC
* @link https : //github.com/GeyserMC/Geyser
2020-05-23 21:50:04 +00:00
* /
2021-11-20 21:34:30 +00:00
package org.geysermc.geyser.ping ;
2020-05-23 21:50:04 +00:00
2020-07-04 21:35:48 +00:00
import com.fasterxml.jackson.core.JsonParseException ;
import com.fasterxml.jackson.databind.JsonMappingException ;
2023-12-05 23:54:42 +00:00
import org.checkerframework.checker.nullness.qual.Nullable ;
2022-12-21 00:47:45 +00:00
import org.cloudburstmc.nbt.util.VarInts ;
2022-07-11 12:38:26 +00:00
import io.netty.handler.codec.haproxy.HAProxyCommand ;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol ;
import io.netty.util.NetUtil ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.GeyserImpl ;
2022-03-20 02:55:29 +00:00
import org.geysermc.geyser.network.GameProtocol ;
2020-05-23 21:50:04 +00:00
2020-07-04 21:35:48 +00:00
import java.io.ByteArrayOutputStream ;
import java.io.DataInputStream ;
import java.io.DataOutputStream ;
import java.io.IOException ;
2022-07-11 12:38:26 +00:00
import java.net.* ;
2020-05-23 21:50:04 +00:00
import java.util.concurrent.TimeUnit ;
public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough , Runnable {
2022-07-11 12:38:26 +00:00
private static final byte [ ] HAPROXY_BINARY_PREFIX = new byte [ ] { 13 , 10 , 13 , 10 , 0 , 13 , 10 , 81 , 85 , 73 , 84 , 10 } ;
2021-11-20 21:34:30 +00:00
private final GeyserImpl geyser ;
2020-05-23 21:50:04 +00:00
2021-11-20 21:34:30 +00:00
public GeyserLegacyPingPassthrough ( GeyserImpl geyser ) {
this . geyser = geyser ;
2020-05-23 21:50:04 +00:00
}
private GeyserPingInfo pingInfo ;
/ * *
* Start legacy ping passthrough thread
2021-11-20 21:34:30 +00:00
* @param geyser Geyser
2020-05-23 21:50:04 +00:00
* @return GeyserPingPassthrough , or null if not initialized
* /
2023-12-05 23:54:42 +00:00
public static @Nullable IGeyserPingPassthrough init ( GeyserImpl geyser ) {
2021-11-20 21:34:30 +00:00
if ( geyser . getConfig ( ) . isPassthroughMotd ( ) | | geyser . getConfig ( ) . isPassthroughPlayerCounts ( ) ) {
GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough ( geyser ) ;
2020-05-23 21:50:04 +00:00
// Ensure delay is not zero
2021-11-20 21:34:30 +00:00
int interval = ( geyser . getConfig ( ) . getPingPassthroughInterval ( ) = = 0 ) ? 1 : geyser . getConfig ( ) . getPingPassthroughInterval ( ) ;
geyser . getLogger ( ) . debug ( " Scheduling ping passthrough at an interval of " + interval + " second(s). " ) ;
geyser . getScheduledThread ( ) . scheduleAtFixedRate ( pingPassthrough , 1 , interval , TimeUnit . SECONDS ) ;
2020-05-23 21:50:04 +00:00
return pingPassthrough ;
}
return null ;
}
@Override
2020-12-07 19:04:50 +00:00
public GeyserPingInfo getPingInformation ( InetSocketAddress inetSocketAddress ) {
2020-05-23 21:50:04 +00:00
return pingInfo ;
}
@Override
public void run ( ) {
2022-07-11 12:38:26 +00:00
try ( Socket socket = new Socket ( ) ) {
2022-07-09 22:39:02 +00:00
String address = geyser . getConfig ( ) . getRemote ( ) . address ( ) ;
int port = geyser . getConfig ( ) . getRemote ( ) . port ( ) ;
2023-03-10 05:13:20 +00:00
InetSocketAddress endpoint = new InetSocketAddress ( address , port ) ;
socket . connect ( endpoint , 5000 ) ;
2020-07-04 21:35:48 +00:00
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream ( ) ;
2022-07-11 12:38:26 +00:00
try ( DataOutputStream handshake = new DataOutputStream ( byteArrayStream ) ) {
handshake . write ( 0x0 ) ;
2022-07-12 05:29:39 +00:00
VarInts . writeUnsignedInt ( handshake , GameProtocol . getJavaProtocolVersion ( ) ) ;
2022-07-11 12:38:26 +00:00
VarInts . writeUnsignedInt ( handshake , address . length ( ) ) ;
handshake . writeBytes ( address ) ;
handshake . writeShort ( port ) ;
VarInts . writeUnsignedInt ( handshake , 1 ) ;
}
byte [ ] buffer ;
try ( DataOutputStream dataOutputStream = new DataOutputStream ( socket . getOutputStream ( ) ) ) {
if ( geyser . getConfig ( ) . getRemote ( ) . isUseProxyProtocol ( ) ) {
// HAProxy support
// Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78
dataOutputStream . write ( HAPROXY_BINARY_PREFIX ) ;
dataOutputStream . writeByte ( ( 0x02 < < 4 ) | HAProxyCommand . PROXY . byteValue ( ) ) ;
dataOutputStream . writeByte ( socket . getLocalAddress ( ) instanceof Inet4Address ?
HAProxyProxiedProtocol . TCP4 . byteValue ( ) : HAProxyProxiedProtocol . TCP6 . byteValue ( ) ) ;
byte [ ] srcAddrBytes = NetUtil . createByteArrayFromIpAddressString (
( ( InetSocketAddress ) socket . getLocalSocketAddress ( ) ) . getAddress ( ) . getHostAddress ( ) ) ;
2023-03-10 05:13:20 +00:00
byte [ ] dstAddrBytes = NetUtil . createByteArrayFromIpAddressString (
endpoint . getAddress ( ) . getHostAddress ( ) ) ;
2022-07-11 12:38:26 +00:00
dataOutputStream . writeShort ( srcAddrBytes . length + dstAddrBytes . length + 4 ) ;
dataOutputStream . write ( srcAddrBytes ) ;
dataOutputStream . write ( dstAddrBytes ) ;
dataOutputStream . writeShort ( ( ( InetSocketAddress ) socket . getLocalSocketAddress ( ) ) . getPort ( ) ) ;
dataOutputStream . writeShort ( port ) ;
}
VarInts . writeUnsignedInt ( dataOutputStream , byteArrayStream . size ( ) ) ;
dataOutputStream . write ( byteArrayStream . toByteArray ( ) ) ;
dataOutputStream . writeByte ( 0x01 ) ;
dataOutputStream . writeByte ( 0x00 ) ;
try ( DataInputStream dataInputStream = new DataInputStream ( socket . getInputStream ( ) ) ) {
VarInts . readUnsignedInt ( dataInputStream ) ;
VarInts . readUnsignedInt ( dataInputStream ) ;
int length = VarInts . readUnsignedInt ( dataInputStream ) ;
buffer = new byte [ length ] ;
dataInputStream . readFully ( buffer ) ;
dataOutputStream . writeByte ( 0x09 ) ;
dataOutputStream . writeByte ( 0x01 ) ;
dataOutputStream . writeLong ( System . currentTimeMillis ( ) ) ;
VarInts . readUnsignedInt ( dataInputStream ) ;
}
}
this . pingInfo = GeyserImpl . JSON_MAPPER . readValue ( buffer , GeyserPingInfo . class ) ;
2020-07-04 21:35:48 +00:00
} catch ( SocketTimeoutException | ConnectException ex ) {
this . pingInfo = null ;
2021-11-20 21:34:30 +00:00
this . geyser . getLogger ( ) . debug ( " Connection timeout for ping passthrough. " ) ;
2020-07-04 21:35:48 +00:00
} catch ( JsonParseException | JsonMappingException ex ) {
2021-11-20 21:34:30 +00:00
this . geyser . getLogger ( ) . error ( " Failed to parse json when pinging server! " , ex ) ;
2023-12-07 19:27:25 +00:00
} catch ( UnknownHostException ex ) {
// Don't reset pingInfo, as we want to keep the last known value
this . geyser . getLogger ( ) . warning ( " Unable to resolve remote host! Is the remote server down or invalid? " ) ;
2020-07-04 21:35:48 +00:00
} catch ( IOException e ) {
2022-07-11 12:38:26 +00:00
this . geyser . getLogger ( ) . error ( " IO error while trying to use legacy ping passthrough " , e ) ;
2020-05-23 21:50:04 +00:00
}
}
}