3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-10-01 23:50:11 +02:00

Make sure that the time we use is always the same across servers

Dieser Commit ist enthalten in:
Tim203 2021-05-26 01:55:58 +02:00
Ursprung 5c76bd8544
Commit cfa2805e00
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 064EE9F5BF7C3EE8
6 geänderte Dateien mit 179 neuen und 11 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.floodgate.time;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
/*
* Thanks:
* https://datatracker.ietf.org/doc/html/rfc1769
* https://github.com/jonsagara/SimpleNtpClient
* https://stackoverflow.com/a/29138806
*/
public final class SntpClientUtils {
private static final int NTP_PORT = 123;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_MODE = 3; // client
private static final int NTP_VERSION = 3;
private static final int RECEIVE_TIME_POSITION = 32;
private static final long NTP_TIME_OFFSET = ((365L * 70L) + 17L) * 24L * 60L * 60L;
public static long requestTimeOffset(String host, int timeout) {
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
ByteBuffer buff = ByteBuffer.allocate(NTP_PACKET_SIZE);
DatagramPacket request = new DatagramPacket(
buff.array(), NTP_PACKET_SIZE, address, NTP_PORT
);
// mode is in the least signification 3 bits
// version is in bits 3-5
buff.put((byte) (NTP_MODE | (NTP_VERSION << 3)));
long originateTime = System.currentTimeMillis();
socket.send(request);
DatagramPacket response = new DatagramPacket(buff.array(), NTP_PACKET_SIZE);
socket.receive(response);
long responseTime = System.currentTimeMillis();
// everything before isn't important for us
buff.position(RECEIVE_TIME_POSITION);
long receiveTime = readTimestamp(buff);
long transmitTime = readTimestamp(buff);
return ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
} catch (Exception ignored) {
}
return Long.MIN_VALUE;
}
private static long readTimestamp(ByteBuffer buffer) {
//todo look into the ntp 2036 problem
long seconds = buffer.getInt() & 0xffffffffL;
long fraction = buffer.getInt() & 0xffffffffL;
return ((seconds - NTP_TIME_OFFSET) * 1000) + ((fraction * 1000) / 0x100000000L);
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.floodgate.time;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class TimeSyncer {
private final ExecutorService executorService;
private long timeOffset = Long.MIN_VALUE; // value when it failed to get the offset
public TimeSyncer(String timeServer) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleWithFixedDelay(() -> {
// 5 tries to get the time offset
for (int i = 0; i < 5; i++) {
long offset = SntpClientUtils.requestTimeOffset(timeServer, 3000);
if (offset != Long.MIN_VALUE) {
timeOffset = offset;
return;
}
}
}, 0, 30, TimeUnit.MINUTES);
executorService = service;
}
public void shutdown() {
executorService.shutdown();
}
public long getTimeOffset() {
return timeOffset;
}
}

Datei anzeigen

@ -28,6 +28,7 @@ package org.geysermc.floodgate.util;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.time.TimeSyncer;
/** /**
* This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This * This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This
@ -56,20 +57,28 @@ public final class BedrockData implements Cloneable {
private final long timestamp; private final long timestamp;
private final int dataLength; private final int dataLength;
public static BedrockData of(String version, String username, String xuid, int deviceOs, public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip, String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId, LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
String verifyCode) { String verifyCode, TimeSyncer timeSyncer) {
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode, long realMillis = System.currentTimeMillis();
System.currentTimeMillis(), EXPECTED_LENGTH); if (timeSyncer.getTimeOffset() != Long.MIN_VALUE) {
realMillis += timeSyncer.getTimeOffset();
} }
public static BedrockData of(String version, String username, String xuid, int deviceOs, return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode,
realMillis, EXPECTED_LENGTH);
}
public static BedrockData of(
String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip, String languageCode, int uiProfile, int inputMode, String ip,
int subscribeId, String verifyCode) { int subscribeId, String verifyCode, TimeSyncer timeSyncer) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null, return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
false, subscribeId, verifyCode); false, subscribeId, verifyCode, timeSyncer);
} }
public static BedrockData fromString(String data) { public static BedrockData fromString(String data) {

Datei anzeigen

@ -62,6 +62,7 @@ import org.geysermc.floodgate.crypto.AesCipher;
import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.Base64Topping;
import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.time.TimeSyncer;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import javax.naming.directory.Attribute; import javax.naming.directory.Attribute;
@ -79,7 +80,6 @@ import java.util.concurrent.TimeUnit;
@Getter @Getter
public class GeyserConnector { public class GeyserConnector {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper() public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
.enable(JsonParser.Feature.IGNORE_UNDEFINED) .enable(JsonParser.Feature.IGNORE_UNDEFINED)
.enable(JsonParser.Feature.ALLOW_COMMENTS) .enable(JsonParser.Feature.ALLOW_COMMENTS)
@ -106,6 +106,7 @@ public class GeyserConnector {
@Setter @Setter
private AuthType defaultAuthType; private AuthType defaultAuthType;
private TimeSyncer timeSyncer;
private FloodgateCipher cipher; private FloodgateCipher cipher;
private FloodgateSkinUploader skinUploader; private FloodgateSkinUploader skinUploader;
@ -198,6 +199,7 @@ public class GeyserConnector {
defaultAuthType = AuthType.getByName(config.getRemote().getAuthType()); defaultAuthType = AuthType.getByName(config.getRemote().getAuthType());
if (defaultAuthType == AuthType.FLOODGATE) { if (defaultAuthType == AuthType.FLOODGATE) {
timeSyncer = new TimeSyncer(Constants.NTP_SERVER);
try { try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping()); cipher = new AesCipher(new Base64Topping());
@ -353,6 +355,7 @@ public class GeyserConnector {
generalThreadPool.shutdown(); generalThreadPool.shutdown();
bedrockServer.close(); bedrockServer.close();
timeSyncer.shutdown();
players.clear(); players.clear();
defaultAuthType = null; defaultAuthType = null;
this.getCommandManager().getCommands().clear(); this.getCommandManager().getCommands().clear();
@ -431,6 +434,10 @@ public class GeyserConnector {
return bootstrap.getWorldManager(); return bootstrap.getWorldManager();
} }
public TimeSyncer getTimeSyncer() {
return timeSyncer;
}
/** /**
* Whether to use XML reflections in the jar or manually find the reflections. * Whether to use XML reflections in the jar or manually find the reflections.
* Will return true if the version number is not 'DEV' and the platform is not Fabric. * Will return true if the version number is not 'DEV' and the platform is not Fabric.

Datei anzeigen

@ -691,7 +691,8 @@ public class GeyserSession implements CommandSender {
clientData.getCurrentInputMode().ordinal(), clientData.getCurrentInputMode().ordinal(),
upstream.getAddress().getAddress().getHostAddress(), upstream.getAddress().getAddress().getHostAddress(),
skinUploader.getId(), skinUploader.getId(),
skinUploader.getVerifyCode() skinUploader.getVerifyCode(),
connector.getTimeSyncer()
).toString()); ).toString());
} catch (Exception e) { } catch (Exception e) {
connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e);

Datei anzeigen

@ -30,6 +30,7 @@ import java.net.URISyntaxException;
public final class Constants { public final class Constants {
public static final URI SKIN_UPLOAD_URI; public static final URI SKIN_UPLOAD_URI;
public static final String NTP_SERVER = "time.cloudflare.com";
static { static {
URI skinUploadUri = null; URI skinUploadUri = null;