Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-24 15:20:25 +01:00
Allow configuration of RakNet limits (#4532)
* Allow configuration of RakNet limits Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Validate packet limiter system properties Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>
Dieser Commit ist enthalten in:
Ursprung
fbafdbb2a7
Commit
c9ca4c82f7
@ -40,9 +40,11 @@ import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.network.CIDRMatcher;
|
||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.WebUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@ -233,7 +235,18 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
List<CIDRMatcher> matchers = this.whitelistedIPsMatchers;
|
||||
if (matchers == null) {
|
||||
synchronized (this) {
|
||||
this.whitelistedIPsMatchers = matchers = proxyProtocolWhitelistedIPs.stream()
|
||||
// Check if proxyProtocolWhitelistedIPs contains URLs we need to fetch and parse by line
|
||||
List<String> whitelistedCIDRs = new ArrayList<>();
|
||||
for (String ip: proxyProtocolWhitelistedIPs) {
|
||||
if (!ip.startsWith("http")) {
|
||||
whitelistedCIDRs.add(ip);
|
||||
continue;
|
||||
}
|
||||
|
||||
WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add);
|
||||
}
|
||||
|
||||
this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream()
|
||||
.map(CIDRMatcher::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import net.jodah.expiringmap.ExpiringMap;
|
||||
import org.cloudburstmc.netty.channel.raknet.RakChannelFactory;
|
||||
import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
|
||||
import org.cloudburstmc.netty.handler.codec.raknet.server.RakServerOfflineHandler;
|
||||
import org.cloudburstmc.netty.handler.codec.raknet.server.RakServerRateLimiter;
|
||||
import org.cloudburstmc.protocol.bedrock.BedrockPong;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.defaults.ConnectionTestCommand;
|
||||
@ -71,6 +72,10 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_GLOBAL_PACKET_LIMIT;
|
||||
import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_OFFLINE_PACKET_LIMIT;
|
||||
import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_PACKET_LIMIT;
|
||||
|
||||
public final class GeyserServer {
|
||||
private static final boolean PRINT_DEBUG_PINGS = Boolean.parseBoolean(System.getProperty("Geyser.PrintPingsInDebugMode", "true"));
|
||||
|
||||
@ -141,23 +146,31 @@ public final class GeyserServer {
|
||||
bootstrapFutures = new ChannelFuture[listenCount];
|
||||
for (int i = 0; i < listenCount; i++) {
|
||||
ChannelFuture future = bootstrap.bind(address);
|
||||
addHandlers(future);
|
||||
modifyHandlers(future);
|
||||
bootstrapFutures[i] = future;
|
||||
}
|
||||
|
||||
return Bootstraps.allOf(bootstrapFutures);
|
||||
}
|
||||
|
||||
private void addHandlers(ChannelFuture future) {
|
||||
private void modifyHandlers(ChannelFuture future) {
|
||||
Channel channel = future.channel();
|
||||
// Add our ping handler
|
||||
channel.pipeline()
|
||||
.addFirst(RakConnectionRequestHandler.NAME, new RakConnectionRequestHandler(this))
|
||||
.addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this));
|
||||
|
||||
// Add proxy handler
|
||||
if (this.geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
||||
boolean isProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol();
|
||||
if (isProxyProtocol) {
|
||||
channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler());
|
||||
}
|
||||
|
||||
boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty();
|
||||
if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) {
|
||||
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
|
||||
channel.pipeline().remove(RakServerRateLimiter.NAME);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@ -199,11 +212,26 @@ public final class GeyserServer {
|
||||
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
|
||||
playerGroup = serverInitializer.getEventLoopGroup();
|
||||
this.geyser.getLogger().debug("Setting MTU to " + this.geyser.getConfig().getMtu());
|
||||
|
||||
int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT);
|
||||
this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit);
|
||||
|
||||
boolean isWhitelistedProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol()
|
||||
&& !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty();
|
||||
int rakOfflinePacketLimit = positivePropOrDefault("Geyser.RakOfflinePacketLimit", isWhitelistedProxyProtocol ? Integer.MAX_VALUE : DEFAULT_OFFLINE_PACKET_LIMIT);
|
||||
this.geyser.getLogger().debug("Setting RakNet offline packet limit to " + rakOfflinePacketLimit);
|
||||
|
||||
int rakGlobalPacketLimit = positivePropOrDefault("Geyser.RakGlobalPacketLimit", DEFAULT_GLOBAL_PACKET_LIMIT);
|
||||
this.geyser.getLogger().debug("Setting RakNet global packet limit to " + rakGlobalPacketLimit);
|
||||
|
||||
return new ServerBootstrap()
|
||||
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel()))
|
||||
.group(group, childGroup)
|
||||
.option(RakChannelOption.RAK_HANDLE_PING, true)
|
||||
.option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu())
|
||||
.option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit)
|
||||
.option(RakChannelOption.RAK_OFFLINE_PACKET_LIMIT, rakOfflinePacketLimit)
|
||||
.option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
|
||||
.childHandler(serverInitializer);
|
||||
}
|
||||
|
||||
@ -352,6 +380,27 @@ public final class GeyserServer {
|
||||
}
|
||||
}
|
||||
|
||||
private static int positivePropOrDefault(String property, int defaultValue) {
|
||||
String value = System.getProperty(property);
|
||||
try {
|
||||
int parsed = value != null ? Integer.parseInt(value) : defaultValue;
|
||||
|
||||
if (parsed < 1) {
|
||||
GeyserImpl.getInstance().getLogger().warning(
|
||||
"Non-postive integer value for " + property + ": " + value + ". Using default value: " + defaultValue
|
||||
);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
} catch (NumberFormatException e) {
|
||||
GeyserImpl.getInstance().getLogger().warning(
|
||||
"Invalid integer value for " + property + ": " + value + ". Using default value: " + defaultValue
|
||||
);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static Transport compatibleTransport() {
|
||||
TransportHelper.TransportMethod transportMethod = TransportHelper.determineTransportMethod();
|
||||
if (transportMethod == TransportHelper.TransportMethod.EPOLL) {
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.annotation.Nulls;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import org.geysermc.geyser.GeyserBootstrap;
|
||||
@ -56,6 +57,8 @@ public class FileUtils {
|
||||
*/
|
||||
public static <T> T loadConfig(File src, Class<T> valueType) throws IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory())
|
||||
// Allow inference of single values as arrays
|
||||
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
|
||||
return objectMapper.readValue(src, valueType);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WebUtils {
|
||||
|
||||
@ -176,6 +177,13 @@ public class WebUtils {
|
||||
return connectionToString(con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a SRV record for the given address
|
||||
*
|
||||
* @param geyser Geyser instance
|
||||
* @param remoteAddress Address to find the SRV record for
|
||||
* @return The SRV record or null if not found
|
||||
*/
|
||||
public static String @Nullable [] findSrvRecord(GeyserImpl geyser, String remoteAddress) {
|
||||
try {
|
||||
// Searches for a server address and a port from a SRV record of the specified host name
|
||||
@ -193,4 +201,26 @@ public class WebUtils {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stream of lines from the given URL
|
||||
*
|
||||
* @param reqURL URL to fetch
|
||||
* @return Stream of lines from the URL or an empty stream if the request fails
|
||||
*/
|
||||
public static Stream<String> getLineStream(String reqURL) {
|
||||
try {
|
||||
URL url = new URL(reqURL);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION); // Otherwise Java 8 fails on checking updates
|
||||
con.setConnectTimeout(10000);
|
||||
con.setReadTimeout(10000);
|
||||
|
||||
return connectionToString(con).lines();
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Error while trying to get a stream from " + reqURL, e);
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ bedrock:
|
||||
# A list of allowed PROXY protocol speaking proxy IP addresses/subnets. Only effective when "enable-proxy-protocol" is enabled, and
|
||||
# should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.).
|
||||
# Keeping this list empty means there is no IP address whitelist.
|
||||
# Both IP addresses and subnets are supported.
|
||||
#proxy-protocol-whitelisted-ips: [ "127.0.0.1", "172.18.0.0/16" ]
|
||||
# IP addresses, subnets, and links to plain text files are supported.
|
||||
#proxy-protocol-whitelisted-ips: [ "127.0.0.1", "172.18.0.0/16", "https://example.com/whitelist.txt" ]
|
||||
remote:
|
||||
# The IP address of the remote (Java Edition) server
|
||||
# If it is "auto", for standalone version the remote address will be set to 127.0.0.1,
|
||||
|
@ -11,7 +11,7 @@ gson = "2.3.1" # Provided by Spigot 1.8.8
|
||||
websocket = "1.5.1"
|
||||
protocol = "3.0.0.Beta1-20240313.120922-126"
|
||||
protocol-connection = "3.0.0.Beta1-20240313.120922-125"
|
||||
raknet = "1.0.0.CR1-20240330.101522-15"
|
||||
raknet = "1.0.0.CR1-20240330.103819-16"
|
||||
blockstateupdater="1.20.70-20240303.125052-2"
|
||||
mcauthlib = "d9d773e"
|
||||
mcprotocollib = "1.20.4-2-20240116.220521-7"
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren