3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-09-29 14:40:21 +02:00

Cherry-pick full Unix domain socket support from b00389029f

Closes #991. We can't properly support this without an API break, so let's just do it in 5.0.0.
Dieser Commit ist enthalten in:
Andrew Steinborn 2020-10-22 02:01:29 -04:00
Ursprung 02ed711573
Commit 771dfa8d0e
18 geänderte Dateien mit 218 neuen und 89 gelöschten Zeilen

Datei anzeigen

@ -9,22 +9,22 @@ package com.velocitypowered.api.event.proxy;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ListenerType; import com.velocitypowered.api.network.ListenerType;
import java.net.InetSocketAddress; import java.net.SocketAddress;
/** /**
* This event is fired by the proxy after a listener starts accepting connections. * This event is fired by the proxy after a listener starts accepting connections.
*/ */
public final class ListenerBoundEvent { public final class ListenerBoundEvent {
private final InetSocketAddress address; private final SocketAddress address;
private final ListenerType listenerType; private final ListenerType listenerType;
public ListenerBoundEvent(InetSocketAddress address, ListenerType listenerType) { public ListenerBoundEvent(SocketAddress address, ListenerType listenerType) {
this.address = Preconditions.checkNotNull(address, "address"); this.address = Preconditions.checkNotNull(address, "address");
this.listenerType = Preconditions.checkNotNull(listenerType, "listenerType"); this.listenerType = Preconditions.checkNotNull(listenerType, "listenerType");
} }
public InetSocketAddress getAddress() { public SocketAddress getAddress() {
return address; return address;
} }

Datei anzeigen

@ -9,22 +9,22 @@ package com.velocitypowered.api.event.proxy;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ListenerType; import com.velocitypowered.api.network.ListenerType;
import java.net.InetSocketAddress; import java.net.SocketAddress;
/** /**
* This event is fired by the proxy before the proxy stops accepting connections. * This event is fired by the proxy before the proxy stops accepting connections.
*/ */
public final class ListenerCloseEvent { public final class ListenerCloseEvent {
private final InetSocketAddress address; private final SocketAddress address;
private final ListenerType listenerType; private final ListenerType listenerType;
public ListenerCloseEvent(InetSocketAddress address, ListenerType listenerType) { public ListenerCloseEvent(SocketAddress address, ListenerType listenerType) {
this.address = Preconditions.checkNotNull(address, "address"); this.address = Preconditions.checkNotNull(address, "address");
this.listenerType = Preconditions.checkNotNull(listenerType, "listenerType"); this.listenerType = Preconditions.checkNotNull(listenerType, "listenerType");
} }
public InetSocketAddress getAddress() { public SocketAddress getAddress() {
return address; return address;
} }

Datei anzeigen

@ -9,6 +9,7 @@ package com.velocitypowered.api.proxy;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Optional; import java.util.Optional;
/** /**
@ -17,11 +18,11 @@ import java.util.Optional;
public interface InboundConnection { public interface InboundConnection {
/** /**
* Returns the player's IP address. * Returns the player's remote address.
* *
* @return the player's IP * @return the player's remote address
*/ */
InetSocketAddress getRemoteAddress(); SocketAddress getRemoteAddress();
/** /**
* Returns the hostname that the user entered into the client, if applicable. * Returns the hostname that the user entered into the client, if applicable.

Datei anzeigen

@ -31,7 +31,6 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.text.event.HoverEventSource;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**

Datei anzeigen

@ -18,7 +18,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.api.util.ProxyVersion;
import java.net.InetSocketAddress; import java.net.SocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -179,7 +179,7 @@ public interface ProxyServer extends Audience {
* *
* @return the address the proxy is bound to * @return the address the proxy is bound to
*/ */
InetSocketAddress getBoundAddress(); SocketAddress getBoundAddress();
/** /**
* Gets the {@link ProxyConfig} instance. * Gets the {@link ProxyConfig} instance.

Datei anzeigen

@ -8,7 +8,7 @@
package com.velocitypowered.api.proxy.server; package com.velocitypowered.api.proxy.server;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.net.InetSocketAddress; import java.net.SocketAddress;
import java.util.Objects; import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -19,7 +19,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class ServerInfo implements Comparable<ServerInfo> { public final class ServerInfo implements Comparable<ServerInfo> {
private final String name; private final String name;
private final InetSocketAddress address; private final SocketAddress address;
/** /**
* Creates a new ServerInfo object. * Creates a new ServerInfo object.
@ -27,7 +27,7 @@ public final class ServerInfo implements Comparable<ServerInfo> {
* @param name the name for the server * @param name the name for the server
* @param address the address of the server to connect to * @param address the address of the server to connect to
*/ */
public ServerInfo(String name, InetSocketAddress address) { public ServerInfo(String name, SocketAddress address) {
this.name = Preconditions.checkNotNull(name, "name"); this.name = Preconditions.checkNotNull(name, "name");
this.address = Preconditions.checkNotNull(address, "address"); this.address = Preconditions.checkNotNull(address, "address");
} }
@ -36,7 +36,7 @@ public final class ServerInfo implements Comparable<ServerInfo> {
return name; return name;
} }
public final InetSocketAddress getAddress() { public final SocketAddress getAddress() {
return address; return address;
} }

Datei anzeigen

@ -70,6 +70,7 @@ import io.netty.channel.EventLoopGroup;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.KeyPair; import java.security.KeyPair;
@ -231,16 +232,18 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
// init console permissions after plugins are loaded // init console permissions after plugins are loaded
console.setupPermissions(); console.setupPermissions();
final SocketAddress bindAddr = configuration.getBind();
final Integer port = this.options.getPort(); final Integer port = this.options.getPort();
if (port != null) { if (port != null && bindAddr instanceof InetSocketAddress) {
logger.debug("Overriding bind port to {} from command line option", port); logger.debug("Overriding bind port to {} from command line option", port);
this.cm.bind(new InetSocketAddress(configuration.getBind().getHostString(), port)); this.cm.bind(new InetSocketAddress(((InetSocketAddress) bindAddr).getHostString(), port));
} else { } else {
this.cm.bind(configuration.getBind()); this.cm.bind(configuration.getBind());
} }
if (configuration.isQueryEnabled()) { if (configuration.isQueryEnabled() && bindAddr instanceof InetSocketAddress) {
this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort()); this.cm.queryBind(((InetSocketAddress) bindAddr).getHostString(),
configuration.getQueryPort());
} }
Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics());
@ -362,8 +365,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
logger.info("Loaded {} plugins", pluginManager.getPlugins().size()); logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
} }
public Bootstrap createBootstrap(@Nullable EventLoopGroup group) { public Bootstrap createBootstrap(@Nullable EventLoopGroup group, SocketAddress target) {
return this.cm.createWorker(group); return this.cm.createWorker(group, target);
} }
public ChannelInitializer<Channel> getBackendChannelInitializer() { public ChannelInitializer<Channel> getBackendChannelInitializer() {
@ -443,7 +446,9 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
} }
// If we have a new bind address, bind to it // If we have a new bind address, bind to it
if (!configuration.getBind().equals(newConfiguration.getBind())) { SocketAddress oldBind = configuration.getBind();
SocketAddress newBind = newConfiguration.getBind();
if (!configuration.getBind().equals(newBind)) {
this.cm.bind(newConfiguration.getBind()); this.cm.bind(newConfiguration.getBind());
this.cm.close(configuration.getBind()); this.cm.close(configuration.getBind());
} }
@ -451,12 +456,13 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
boolean queryPortChanged = newConfiguration.getQueryPort() != configuration.getQueryPort(); boolean queryPortChanged = newConfiguration.getQueryPort() != configuration.getQueryPort();
boolean queryAlreadyEnabled = configuration.isQueryEnabled(); boolean queryAlreadyEnabled = configuration.isQueryEnabled();
boolean queryEnabled = newConfiguration.isQueryEnabled(); boolean queryEnabled = newConfiguration.isQueryEnabled();
if ((!queryEnabled && queryAlreadyEnabled) || queryPortChanged) { if (oldBind instanceof InetSocketAddress
&& ((!queryEnabled && queryAlreadyEnabled) || queryPortChanged)) {
this.cm.close(new InetSocketAddress( this.cm.close(new InetSocketAddress(
configuration.getBind().getHostString(), configuration.getQueryPort())); ((InetSocketAddress) oldBind).getHostString(), configuration.getQueryPort()));
} }
if (queryEnabled && queryPortChanged) { if (queryEnabled && queryPortChanged && configuration.getBind() instanceof InetSocketAddress) {
this.cm.queryBind(newConfiguration.getBind().getHostString(), this.cm.queryBind(((InetSocketAddress) newConfiguration.getBind()).getHostString(),
newConfiguration.getQueryPort()); newConfiguration.getQueryPort());
} }
@ -718,7 +724,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
} }
@Override @Override
public InetSocketAddress getBoundAddress() { public SocketAddress getBoundAddress() {
if (configuration == null) { if (configuration == null) {
throw new IllegalStateException( throw new IllegalStateException(
"No configuration"); // even though you'll never get the chance... heh, heh "No configuration"); // even though you'll never get the chance... heh, heh

Datei anzeigen

@ -32,7 +32,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetSocketAddress; import java.net.SocketAddress;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@ -247,7 +247,7 @@ public class VelocityConfiguration implements ProxyConfig {
} }
} }
public InetSocketAddress getBind() { public SocketAddress getBind() {
return AddressUtil.parseAndResolveAddress(bind); return AddressUtil.parseAndResolveAddress(bind);
} }

Datei anzeigen

@ -35,6 +35,9 @@ import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Optional; import java.util.Optional;
import java.util.StringJoiner; import java.util.StringJoiner;
import net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
@ -94,8 +97,16 @@ public class BungeeCordMessageResponder {
ByteBuf buf = Unpooled.buffer(); ByteBuf buf = Unpooled.buffer();
ByteBufDataOutput out = new ByteBufDataOutput(buf); ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("IP"); out.writeUTF("IP");
out.writeUTF(player.getRemoteAddress().getHostString());
out.writeInt(player.getRemoteAddress().getPort()); SocketAddress address = player.getRemoteAddress();
if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
out.writeInt(serverInetAddr.getPort());
} else {
out.writeUTF("unix://" + ((DomainSocketAddress) address).path());
out.writeInt(0);
}
sendResponseOnConnection(buf); sendResponseOnConnection(buf);
} }
@ -234,8 +245,15 @@ public class BungeeCordMessageResponder {
out.writeUTF("IPOther"); out.writeUTF("IPOther");
out.writeUTF(player.getUsername()); out.writeUTF(player.getUsername());
out.writeUTF(player.getRemoteAddress().getHostString()); SocketAddress address = player.getRemoteAddress();
out.writeInt(player.getRemoteAddress().getPort()); if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
out.writeInt(serverInetAddr.getPort());
} else {
out.writeUTF("unix://" + ((DomainSocketAddress) address).path());
out.writeInt(0);
}
sendResponseOnConnection(buf); sendResponseOnConnection(buf);
}); });
@ -248,8 +266,15 @@ public class BungeeCordMessageResponder {
out.writeUTF("ServerIP"); out.writeUTF("ServerIP");
out.writeUTF(info.getServerInfo().getName()); out.writeUTF(info.getServerInfo().getName());
out.writeUTF(info.getServerInfo().getAddress().getHostString()); SocketAddress address = info.getServerInfo().getAddress();
out.writeShort(info.getServerInfo().getAddress().getPort()); if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
out.writeShort(serverInetAddr.getPort());
} else {
out.writeUTF("unix://" + ((DomainSocketAddress) address).path());
out.writeShort(0);
}
sendResponseOnConnection(buf); sendResponseOnConnection(buf);
}); });

Datei anzeigen

@ -45,6 +45,8 @@ import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -98,9 +100,10 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
CompletableFuture<Impl> result = new CompletableFuture<>(); CompletableFuture<Impl> result = new CompletableFuture<>();
// Note: we use the event loop for the connection the player is on. This reduces context // Note: we use the event loop for the connection the player is on. This reduces context
// switches. // switches.
server.createBootstrap(proxyPlayer.getConnection().eventLoop()) SocketAddress destinationAddress = registeredServer.getServerInfo().getAddress();
server.createBootstrap(proxyPlayer.getConnection().eventLoop(), destinationAddress)
.handler(server.getBackendChannelInitializer()) .handler(server.getBackendChannelInitializer())
.connect(registeredServer.getServerInfo().getAddress()) .connect(destinationAddress)
.addListener((ChannelFutureListener) future -> { .addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) { if (future.isSuccess()) {
connection = new MinecraftConnection(future.channel(), server); connection = new MinecraftConnection(future.channel(), server);
@ -124,23 +127,37 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
} }
String getPlayerRemoteAddressAsString() { String getPlayerRemoteAddressAsString() {
final String addr = proxyPlayer.getRemoteAddress().getAddress().getHostAddress(); final SocketAddress address = proxyPlayer.getRemoteAddress();
int ipv6ScopeIdx = addr.indexOf('%'); if (!(address instanceof InetSocketAddress)) {
if (ipv6ScopeIdx == -1) { return address.toString();
return addr;
} else {
return addr.substring(0, ipv6ScopeIdx);
} }
final String host = ((InetSocketAddress) address).getAddress().getHostAddress();
int ipv6ScopeIdx = host.indexOf('%');
if (ipv6ScopeIdx == -1) {
return host;
}
return host.substring(0, ipv6ScopeIdx);
}
private String getHandshakeRemoteAddress() {
return proxyPlayer.getVirtualHost()
.map(InetSocketAddress::getHostString)
.or(() -> Optional.of(registeredServer.getServerInfo().getAddress())
.filter(addr -> addr instanceof InetSocketAddress)
.map(addr -> ((InetSocketAddress) addr).getHostString()))
.orElse("");
} }
private String createLegacyForwardingAddress(UnaryOperator<List<Property>> propertiesTransform) { private String createLegacyForwardingAddress(UnaryOperator<List<Property>> propertiesTransform) {
// BungeeCord IP forwarding is simply a special injection after the "address" in the handshake, // BungeeCord IP forwarding is simply a special injection after the "address" in the handshake,
// separated by \0 (the null byte). In order, you send the original host, the player's IP, their // separated by \0 (the null byte). In order, you send the original host, the player's IP, their
// UUID (undashed), and if you are in online-mode, their login properties (from Mojang). // UUID (undashed), and if you are in online-mode, their login properties (from Mojang).
SocketAddress playerRemoteAddress = proxyPlayer.getRemoteAddress();
if (!(playerRemoteAddress instanceof InetSocketAddress)) {
return getHandshakeRemoteAddress();
}
StringBuilder data = new StringBuilder() StringBuilder data = new StringBuilder()
.append(proxyPlayer.getVirtualHost() .append(getHandshakeRemoteAddress())
.orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getHostString())
.append('\0') .append('\0')
.append(getPlayerRemoteAddressAsString()) .append(getPlayerRemoteAddressAsString())
.append('\0') .append('\0')
@ -171,10 +188,6 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
// Initiate the handshake. // Initiate the handshake.
ProtocolVersion protocolVersion = proxyPlayer.getConnection().getProtocolVersion(); ProtocolVersion protocolVersion = proxyPlayer.getConnection().getProtocolVersion();
String playerVhost = proxyPlayer.getVirtualHost()
.orElseGet(() -> registeredServer.getServerInfo().getAddress())
.getHostString();
Handshake handshake = new Handshake(); Handshake handshake = new Handshake();
handshake.setNextStatus(StateRegistry.LOGIN_ID); handshake.setNextStatus(StateRegistry.LOGIN_ID);
handshake.setProtocolVersion(protocolVersion); handshake.setProtocolVersion(protocolVersion);
@ -184,12 +197,15 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
byte[] secret = server.getConfiguration().getForwardingSecret(); byte[] secret = server.getConfiguration().getForwardingSecret();
handshake.setServerAddress(createBungeeGuardForwardingAddress(secret)); handshake.setServerAddress(createBungeeGuardForwardingAddress(secret));
} else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) {
handshake.setServerAddress(playerVhost + HANDSHAKE_HOSTNAME_TOKEN); handshake.setServerAddress(getHandshakeRemoteAddress() + HANDSHAKE_HOSTNAME_TOKEN);
} else { } else {
handshake.setServerAddress(playerVhost); handshake.setServerAddress(getHandshakeRemoteAddress());
} }
handshake.setPort(registeredServer.getServerInfo().getAddress().getPort()); SocketAddress destinationAddr = registeredServer.getServerInfo().getAddress();
if (destinationAddr instanceof InetSocketAddress) {
handshake.setPort(((InetSocketAddress) destinationAddr).getPort());
}
mc.delayedWrite(handshake); mc.delayedWrite(handshake);
mc.setProtocolVersion(protocolVersion); mc.setProtocolVersion(protocolVersion);

Datei anzeigen

@ -81,6 +81,7 @@ import com.velocitypowered.proxy.util.collect.CappedSet;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -304,8 +305,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
} }
@Override @Override
public InetSocketAddress getRemoteAddress() { public SocketAddress getRemoteAddress() {
return (InetSocketAddress) connection.getRemoteAddress(); return connection.getRemoteAddress();
} }
@Override @Override

Datei anzeigen

@ -38,6 +38,7 @@ import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.epoll.EpollChannelOption;
import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.GlobalEventExecutor;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -57,7 +58,7 @@ public final class ConnectionManager {
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20, private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
1 << 21); 1 << 21);
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class); private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
private final Map<InetSocketAddress, Endpoint> endpoints = new HashMap<>(); private final Map<SocketAddress, Endpoint> endpoints = new HashMap<>();
private final TransportType transportType; private final TransportType transportType;
private final EventLoopGroup bossGroup; private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup; private final EventLoopGroup workerGroup;
@ -113,18 +114,20 @@ public final class ConnectionManager {
* *
* @param address the address to bind to * @param address the address to bind to
*/ */
public void bind(final InetSocketAddress address) { public void bind(final SocketAddress address) {
final ServerBootstrap bootstrap = new ServerBootstrap() final ServerBootstrap bootstrap = new ServerBootstrap()
.channelFactory(this.transportType.serverSocketChannelFactory) .channelFactory(this.transportType.getServerChannelFactory(address))
.group(this.bossGroup, this.workerGroup) .group(this.bossGroup, this.workerGroup)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK) .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
.childHandler(this.serverChannelInitializer.get()) .childHandler(this.serverChannelInitializer.get())
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.IP_TOS, 0x18)
.localAddress(address); .localAddress(address);
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { if (address instanceof InetSocketAddress) {
bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); bootstrap.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.IP_TOS, 0x18);
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) {
bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3);
}
} }
bootstrap.bind() bootstrap.bind()
@ -173,14 +176,15 @@ public final class ConnectionManager {
} }
/** /**
* Creates a TCP {@link Bootstrap} using Velocity's event loops. * Creates a {@link Bootstrap} using Velocity's event loops.
* *
* @param group the event loop group to use. Use {@code null} for the default worker group. * @param group the event loop group to use. Use {@code null} for the default worker group.
* @param target the address the client will connect to
* @return a new {@link Bootstrap} * @return a new {@link Bootstrap}
*/ */
public Bootstrap createWorker(@Nullable EventLoopGroup group) { public Bootstrap createWorker(@Nullable EventLoopGroup group, SocketAddress target) {
Bootstrap bootstrap = new Bootstrap() Bootstrap bootstrap = new Bootstrap()
.channelFactory(this.transportType.socketChannelFactory) .channelFactory(this.transportType.getClientChannelFactory(target))
.option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, .option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
this.server.getConfiguration().getConnectTimeout()) this.server.getConfiguration().getConnectTimeout())
@ -197,7 +201,7 @@ public final class ConnectionManager {
* *
* @param oldBind the endpoint to close * @param oldBind the endpoint to close
*/ */
public void close(InetSocketAddress oldBind) { public void close(SocketAddress oldBind) {
Endpoint endpoint = endpoints.remove(oldBind); Endpoint endpoint = endpoints.remove(oldBind);
// Fire proxy close event to notify plugins of socket close. We block since plugins // Fire proxy close event to notify plugins of socket close. We block since plugins
@ -215,8 +219,8 @@ public final class ConnectionManager {
* Closes all endpoints. * Closes all endpoints.
*/ */
public void shutdown() { public void shutdown() {
for (final Map.Entry<InetSocketAddress, Endpoint> entry : this.endpoints.entrySet()) { for (final Map.Entry<SocketAddress, Endpoint> entry : this.endpoints.entrySet()) {
final InetSocketAddress address = entry.getKey(); final SocketAddress address = entry.getKey();
final Endpoint endpoint = entry.getValue(); final Endpoint endpoint = entry.getValue();
// Fire proxy close event to notify plugins of socket close. We block since plugins // Fire proxy close event to notify plugins of socket close. We block since plugins

Datei anzeigen

@ -18,11 +18,15 @@
package com.velocitypowered.proxy.network; package com.velocitypowered.proxy.network;
import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory; import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory; import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollDomainSocketChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerDomainSocketChannel;
import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
@ -32,6 +36,10 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.ServerDomainSocketChannel;
import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -42,27 +50,37 @@ public enum TransportType {
NIO("NIO", NioServerSocketChannel::new, NIO("NIO", NioServerSocketChannel::new,
NioSocketChannel::new, NioSocketChannel::new,
NioDatagramChannel::new, NioDatagramChannel::new,
null,
null,
(name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))), (name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))),
EPOLL("epoll", EpollServerSocketChannel::new, EPOLL("epoll", EpollServerSocketChannel::new,
EpollSocketChannel::new, EpollSocketChannel::new,
EpollDatagramChannel::new, EpollDatagramChannel::new,
EpollServerDomainSocketChannel::new,
EpollDomainSocketChannel::new,
(name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))); (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type)));
final String name; final String name;
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory; final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory;
final ChannelFactory<? extends SocketChannel> socketChannelFactory; final ChannelFactory<? extends SocketChannel> socketChannelFactory;
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory; final ChannelFactory<? extends DatagramChannel> datagramChannelFactory;
final ChannelFactory<? extends ServerDomainSocketChannel> domainServerSocketChannelFactory;
final ChannelFactory<? extends DomainSocketChannel> domainSocketChannelFactory;
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory; final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory;
TransportType(final String name, TransportType(final String name,
final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory, final ChannelFactory<? extends ServerSocketChannel> serverSocketChannelFactory,
final ChannelFactory<? extends SocketChannel> socketChannelFactory, final ChannelFactory<? extends SocketChannel> socketChannelFactory,
final ChannelFactory<? extends DatagramChannel> datagramChannelFactory, final ChannelFactory<? extends DatagramChannel> datagramChannelFactory,
final ChannelFactory<? extends ServerDomainSocketChannel> domainServerSocketChannelFactory,
final ChannelFactory<? extends DomainSocketChannel> domainSocketChannelFactory,
final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory) { final BiFunction<String, Type, EventLoopGroup> eventLoopGroupFactory) {
this.name = name; this.name = name;
this.serverSocketChannelFactory = serverSocketChannelFactory; this.serverSocketChannelFactory = serverSocketChannelFactory;
this.socketChannelFactory = socketChannelFactory; this.socketChannelFactory = socketChannelFactory;
this.datagramChannelFactory = datagramChannelFactory; this.datagramChannelFactory = datagramChannelFactory;
this.domainServerSocketChannelFactory = domainServerSocketChannelFactory;
this.domainSocketChannelFactory = domainSocketChannelFactory;
this.eventLoopGroupFactory = eventLoopGroupFactory; this.eventLoopGroupFactory = eventLoopGroupFactory;
} }
@ -71,6 +89,40 @@ public enum TransportType {
return this.name; return this.name;
} }
/**
* Returns the channel factory to use to listen on the specified socket address.
*
* @param address the address we want to listen on
* @return the channel factory
*/
public ChannelFactory<? extends ServerChannel> getServerChannelFactory(SocketAddress address) {
if (address instanceof DomainSocketAddress) {
if (this.domainServerSocketChannelFactory == null) {
throw new IllegalArgumentException(
"Domain sockets are not available for non-Linux platforms");
}
return this.domainServerSocketChannelFactory;
}
return this.serverSocketChannelFactory;
}
/**
* Returns the channel factory to use to connect on the specified socket address.
*
* @param address the address we want to connect to
* @return the channel factory
*/
public ChannelFactory<? extends Channel> getClientChannelFactory(SocketAddress address) {
if (address instanceof DomainSocketAddress) {
if (this.domainSocketChannelFactory == null) {
throw new IllegalArgumentException(
"Domain sockets are not available for non-Linux platforms");
}
return this.domainSocketChannelFactory;
}
return this.socketChannelFactory;
}
public EventLoopGroup createEventLoopGroup(final Type type) { public EventLoopGroup createEventLoopGroup(final Type type) {
return this.eventLoopGroupFactory.apply(this.name, type); return this.eventLoopGroupFactory.apply(this.name, type);
} }

Datei anzeigen

@ -35,6 +35,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress;
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.ArrayList;
@ -92,8 +93,8 @@ public class GameSpyQueryHandler extends SimpleChannelInboundHandler<DatagramPac
.map(server.getConfiguration().getQueryMap()) .map(server.getConfiguration().getQueryMap())
.currentPlayers(server.getPlayerCount()) .currentPlayers(server.getPlayerCount())
.maxPlayers(server.getConfiguration().getShowMaxPlayers()) .maxPlayers(server.getConfiguration().getShowMaxPlayers())
.proxyPort(server.getConfiguration().getBind().getPort()) .proxyPort(((InetSocketAddress) server.getConfiguration().getBind()).getPort())
.proxyHost(server.getConfiguration().getBind().getHostString()) .proxyHost(((InetSocketAddress) server.getConfiguration().getBind()).getHostString())
.players(server.getAllPlayers().stream().map(Player::getUsername) .players(server.getAllPlayers().stream().map(Player::getUsername)
.collect(Collectors.toList())) .collect(Collectors.toList()))
.proxyVersion("Velocity") .proxyVersion("Velocity")

Datei anzeigen

@ -29,6 +29,8 @@ import com.velocitypowered.proxy.protocol.packet.StatusRequest;
import com.velocitypowered.proxy.protocol.packet.StatusResponse; import com.velocitypowered.proxy.protocol.packet.StatusResponse;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
/** /**
@ -55,8 +57,16 @@ public class PingSessionHandler implements MinecraftSessionHandler {
public void activated() { public void activated() {
Handshake handshake = new Handshake(); Handshake handshake = new Handshake();
handshake.setNextStatus(StateRegistry.STATUS_ID); handshake.setNextStatus(StateRegistry.STATUS_ID);
handshake.setServerAddress(server.getServerInfo().getAddress().getHostString());
handshake.setPort(server.getServerInfo().getAddress().getPort()); SocketAddress address = server.getServerInfo().getAddress();
if (address instanceof InetSocketAddress) {
InetSocketAddress socketAddr = (InetSocketAddress) address;
handshake.setServerAddress(socketAddr.getHostString());
handshake.setPort(socketAddr.getPort());
} else {
// Just fake it
handshake.setServerAddress("127.0.0.1");
}
handshake.setProtocolVersion(version); handshake.setProtocolVersion(version);
connection.delayedWrite(handshake); connection.delayedWrite(handshake);

Datei anzeigen

@ -106,7 +106,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
throw new IllegalStateException("No Velocity proxy instance available"); throw new IllegalStateException("No Velocity proxy instance available");
} }
CompletableFuture<ServerPing> pingFuture = new CompletableFuture<>(); CompletableFuture<ServerPing> pingFuture = new CompletableFuture<>();
server.createBootstrap(loop) server.createBootstrap(loop, serverInfo.getAddress())
.handler(new ChannelInitializer<Channel>() { .handler(new ChannelInitializer<Channel>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) throws Exception {

Datei anzeigen

@ -19,8 +19,11 @@ package com.velocitypowered.proxy.util;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.unix.DomainSocketAddress;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI; import java.net.URI;
/** /**
@ -35,14 +38,16 @@ public final class AddressUtil {
} }
/** /**
* Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned * Attempts to parse a socket address of the form {@code 127.0.0.1:25565}. The returned
* {@link InetSocketAddress} is not resolved. * {@link SocketAddress} is not resolved if it is a {@link InetSocketAddress}.
* *
* @param ip the IP to parse * @param ip the IP to parse
* @return the parsed address * @return the parsed address
*/ */
public static InetSocketAddress parseAddress(String ip) { public static SocketAddress parseAddress(String ip) {
Preconditions.checkNotNull(ip, "ip"); if (ip.startsWith("unix://") && Epoll.isAvailable()) {
return new DomainSocketAddress(ip.substring("unix://".length()));
}
URI uri = URI.create("tcp://" + ip); URI uri = URI.create("tcp://" + ip);
if (uri.getHost() == null) { if (uri.getHost() == null) {
throw new IllegalStateException("Invalid hostname/IP " + ip); throw new IllegalStateException("Invalid hostname/IP " + ip);
@ -58,13 +63,16 @@ public final class AddressUtil {
} }
/** /**
* Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned * Attempts to parse a socket address of the form {@code 127.0.0.1:25565}. The returned
* {@link InetSocketAddress} is resolved. * {@link SocketAddress} is resolved if it is a {@link InetSocketAddress}.
* *
* @param ip the IP to parse * @param ip the IP to parse
* @return the parsed address * @return the parsed address
*/ */
public static InetSocketAddress parseAndResolveAddress(String ip) { public static SocketAddress parseAndResolveAddress(String ip) {
if (ip.startsWith("unix://") && Epoll.isAvailable()) {
return new DomainSocketAddress(ip.substring("unix://".length()));
}
Preconditions.checkNotNull(ip, "ip"); Preconditions.checkNotNull(ip, "ip");
URI uri = URI.create("tcp://" + ip); URI uri = URI.create("tcp://" + ip);
if (uri.getHost() == null) { if (uri.getHost() == null) {

Datei anzeigen

@ -38,6 +38,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -196,14 +197,19 @@ public enum InformationUtils {
public static JsonObject collectServerInfo(RegisteredServer server) { public static JsonObject collectServerInfo(RegisteredServer server) {
JsonObject info = new JsonObject(); JsonObject info = new JsonObject();
info.addProperty("currentPlayers", server.getPlayersConnected().size()); info.addProperty("currentPlayers", server.getPlayersConnected().size());
InetSocketAddress iaddr = server.getServerInfo().getAddress(); SocketAddress addr = server.getServerInfo().getAddress();
if (iaddr.isUnresolved()) { if (addr instanceof InetSocketAddress) {
// Greetings form Netty 4aa10db9 InetSocketAddress iaddr = (InetSocketAddress) addr;
info.addProperty("host", iaddr.getHostString()); if (iaddr.isUnresolved()) {
// Greetings form Netty 4aa10db9
info.addProperty("host", iaddr.getHostString());
} else {
info.addProperty("host", anonymizeInetAddress(iaddr.getAddress()));
}
info.addProperty("port", iaddr.getPort());
} else { } else {
info.addProperty("host", anonymizeInetAddress(iaddr.getAddress())); info.addProperty("bind", addr.toString());
} }
info.addProperty("port", iaddr.getPort());
return info; return info;
} }