13
0
geforkt von Mirrors/Velocity

Fix various problems with GS4QueryHandler

Dieser Commit ist enthalten in:
Andrew Steinborn 2020-07-16 12:44:02 -04:00
Ursprung 2296a9d8dd
Commit 4f19bfde3d

Datei anzeigen

@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableSet;
import com.velocitypowered.api.event.query.ProxyQueryEvent; import com.velocitypowered.api.event.query.ProxyQueryEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.QueryResponse; import com.velocitypowered.api.proxy.server.QueryResponse;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
@ -19,6 +20,7 @@ import io.netty.channel.socket.DatagramPacket;
import java.net.InetAddress; import java.net.InetAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
@ -33,8 +35,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> { public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private static final Logger logger = LogManager.getLogger(GS4QueryHandler.class);
private static final short QUERY_MAGIC_FIRST = 0xFE; private static final short QUERY_MAGIC_FIRST = 0xFE;
private static final short QUERY_MAGIC_SECOND = 0xFD; private static final short QUERY_MAGIC_SECOND = 0xFD;
private static final byte QUERY_TYPE_HANDSHAKE = 0x09; private static final byte QUERY_TYPE_HANDSHAKE = 0x09;
@ -59,10 +59,6 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
.expireAfterWrite(30, TimeUnit.SECONDS) .expireAfterWrite(30, TimeUnit.SECONDS)
.build(); .build();
private final SecureRandom random; private final SecureRandom random;
private volatile @MonotonicNonNull List<QueryResponse.PluginInformation> pluginInformationList
= null;
private final VelocityServer server; private final VelocityServer server;
public GS4QueryHandler(VelocityServer server) { public GS4QueryHandler(VelocityServer server) {
@ -93,15 +89,10 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
ByteBuf queryMessage = msg.content(); ByteBuf queryMessage = msg.content();
InetAddress senderAddress = msg.sender().getAddress(); InetAddress senderAddress = msg.sender().getAddress();
// Allocate buffer for response
ByteBuf queryResponse = ctx.alloc().buffer();
DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender());
try {
// Verify query packet magic // Verify query packet magic
if (queryMessage.readUnsignedByte() != QUERY_MAGIC_FIRST if (queryMessage.readUnsignedByte() != QUERY_MAGIC_FIRST
|| queryMessage.readUnsignedByte() != QUERY_MAGIC_SECOND) { || queryMessage.readUnsignedByte() != QUERY_MAGIC_SECOND) {
throw new IllegalStateException("Invalid query packet magic"); return;
} }
// Read packet header // Read packet header
@ -115,9 +106,12 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
sessions.put(senderAddress, challengeToken); sessions.put(senderAddress, challengeToken);
// Respond with challenge token // Respond with challenge token
ByteBuf queryResponse = ctx.alloc().buffer();
queryResponse.writeByte(QUERY_TYPE_HANDSHAKE); queryResponse.writeByte(QUERY_TYPE_HANDSHAKE);
queryResponse.writeInt(sessionId); queryResponse.writeInt(sessionId);
writeString(queryResponse, Integer.toString(challengeToken)); writeString(queryResponse, Integer.toString(challengeToken));
DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender());
ctx.writeAndFlush(responsePacket, ctx.voidPromise()); ctx.writeAndFlush(responsePacket, ctx.voidPromise());
break; break;
} }
@ -127,24 +121,24 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
int challengeToken = queryMessage.readInt(); int challengeToken = queryMessage.readInt();
Integer session = sessions.getIfPresent(senderAddress); Integer session = sessions.getIfPresent(senderAddress);
if (session == null || session != challengeToken) { if (session == null || session != challengeToken) {
throw new IllegalStateException("Invalid challenge token"); return;
} }
// Check which query response client expects // Check which query response client expects
if (queryMessage.readableBytes() != 0 && queryMessage.readableBytes() != 4) { if (queryMessage.readableBytes() != 0 && queryMessage.readableBytes() != 4) {
throw new IllegalStateException("Invalid query packet"); return;
} }
// Build query response // Build initial query response
QueryResponse response = createInitialResponse(); QueryResponse response = createInitialResponse();
boolean isBasic = queryMessage.isReadable();
boolean isBasic = queryMessage.readableBytes() == 0;
// Call event and write response // Call event and write response
server.getEventManager() server.getEventManager()
.fire(new ProxyQueryEvent(isBasic ? BASIC : FULL, senderAddress, response)) .fire(new ProxyQueryEvent(isBasic ? BASIC : FULL, senderAddress, response))
.whenCompleteAsync((event, exc) -> { .whenCompleteAsync((event, exc) -> {
// Packet header // Packet header
ByteBuf queryResponse = ctx.alloc().buffer();
queryResponse.writeByte(QUERY_TYPE_STAT); queryResponse.writeByte(QUERY_TYPE_STAT);
queryResponse.writeInt(sessionId); queryResponse.writeInt(sessionId);
@ -169,19 +163,13 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
} }
// Send the response // Send the response
DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender());
ctx.writeAndFlush(responsePacket, ctx.voidPromise()); ctx.writeAndFlush(responsePacket, ctx.voidPromise());
}, ctx.channel().eventLoop()); }, ctx.channel().eventLoop());
break; break;
} }
default: default:
throw new IllegalStateException("Invalid query type: " + type); // Invalid query type - just don't respond
}
} catch (Exception e) {
logger.warn("Error while trying to handle a query packet from {}", msg.sender(), e);
// NB: Only need to explicitly release upon exception, writing the response out will decrement
// the reference count.
responsePacket.release();
} }
} }
@ -191,20 +179,13 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
} }
private List<QueryResponse.PluginInformation> getRealPluginInformation() { private List<QueryResponse.PluginInformation> getRealPluginInformation() {
// Effective Java, Third Edition; Item 83: Use lazy initialization judiciously List<QueryResponse.PluginInformation> result = new ArrayList<>();
List<QueryResponse.PluginInformation> res = pluginInformationList; for (PluginContainer plugin : server.getPluginManager().getPlugins()) {
if (res == null) { PluginDescription description = plugin.getDescription();
synchronized (this) { result.add(QueryResponse.PluginInformation.of(description.getName()
if (pluginInformationList == null) { .orElse(description.getId()), description.getVersion().orElse(null)));
pluginInformationList = res = server.getPluginManager().getPlugins().stream()
.map(PluginContainer::getDescription)
.map(desc -> QueryResponse.PluginInformation
.of(desc.getName().orElse(desc.getId()), desc.getVersion().orElse(null)))
.collect(Collectors.toList());
} }
} return result;
}
return res;
} }
private static class ResponseWriter { private static class ResponseWriter {