diff --git a/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java b/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java index 74e840310..29a0c229c 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java @@ -3,6 +3,7 @@ package com.velocitypowered.api.proxy; import com.velocitypowered.api.network.ProtocolVersion; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Optional; /** @@ -11,11 +12,11 @@ import java.util.Optional; 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. diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 5691756fb..1b93cb637 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -28,7 +28,6 @@ public interface Player extends CommandSource, Identified, InboundConnection, */ String getUsername(); - /** * Returns the player's UUID. * diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java index 0a25db20b..bc159012e 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java @@ -10,7 +10,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.util.ProxyVersion; -import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Collection; import java.util.Optional; import java.util.UUID; @@ -163,7 +163,7 @@ public interface ProxyServer extends Audience { * * @return the address the proxy is bound to */ - InetSocketAddress getBoundAddress(); + SocketAddress getBoundAddress(); /** * Gets the {@link ProxyConfig} instance. diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java index 83e5e33e1..eb54c2902 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerInfo.java @@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy.server; import com.google.common.base.Preconditions; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; @@ -12,7 +13,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; public final class ServerInfo implements Comparable { private final String name; - private final InetSocketAddress address; + private final SocketAddress address; /** * Creates a new ServerInfo object. @@ -20,7 +21,7 @@ public final class ServerInfo implements Comparable { * @param name the name for the server * @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.address = Preconditions.checkNotNull(address, "address"); } @@ -29,7 +30,7 @@ public final class ServerInfo implements Comparable { return name; } - public final InetSocketAddress getAddress() { + public final SocketAddress getAddress() { return address; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 249f8cb40..f329ff1cf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -47,6 +47,7 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -200,16 +201,18 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { // init console permissions after plugins are loaded console.setupPermissions(); + final SocketAddress bindAddr = configuration.getBind(); 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); - this.cm.bind(new InetSocketAddress(configuration.getBind().getHostString(), port)); + this.cm.bind(new InetSocketAddress(((InetSocketAddress) bindAddr).getHostString(), port)); } else { this.cm.bind(configuration.getBind()); } - if (configuration.isQueryEnabled()) { - this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort()); + if (configuration.isQueryEnabled() && bindAddr instanceof InetSocketAddress) { + this.cm.queryBind(((InetSocketAddress) bindAddr).getHostString(), + configuration.getQueryPort()); } Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); @@ -271,8 +274,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { logger.info("Loaded {} plugins", pluginManager.getPlugins().size()); } - public Bootstrap createBootstrap(@Nullable EventLoopGroup group) { - return this.cm.createWorker(group); + public Bootstrap createBootstrap(@Nullable EventLoopGroup group, SocketAddress target) { + return this.cm.createWorker(group, target); } public ChannelInitializer getBackendChannelInitializer() { @@ -348,19 +351,22 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { } // 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.close(configuration.getBind()); } if (configuration.isQueryEnabled() && (!newConfiguration.isQueryEnabled() - || newConfiguration.getQueryPort() != configuration.getQueryPort())) { + || newConfiguration.getQueryPort() != configuration.getQueryPort() + && oldBind instanceof InetSocketAddress)) { this.cm.close(new InetSocketAddress( - configuration.getBind().getHostString(), configuration.getQueryPort())); + ((InetSocketAddress) oldBind).getHostString(), configuration.getQueryPort())); } - if (newConfiguration.isQueryEnabled()) { - this.cm.queryBind(newConfiguration.getBind().getHostString(), + if (newConfiguration.isQueryEnabled() && newBind instanceof InetSocketAddress) { + this.cm.queryBind(((InetSocketAddress) newBind).getHostString(), newConfiguration.getQueryPort()); } @@ -615,7 +621,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { } @Override - public InetSocketAddress getBoundAddress() { + public SocketAddress getBoundAddress() { if (configuration == null) { throw new IllegalStateException( "No configuration"); // even though you'll never get the chance... heh, heh diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index c00bd48ea..6fcd74545 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -16,6 +16,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -212,7 +213,7 @@ public class VelocityConfiguration implements ProxyConfig { } } - public InetSocketAddress getBind() { + public SocketAddress getBind() { return AddressUtil.parseAndResolveAddress(bind); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java index e8896cf63..5b86f33fc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java @@ -16,6 +16,9 @@ import com.velocitypowered.proxy.server.VelocityRegisteredServer; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.netty.buffer.ByteBuf; 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.StringJoiner; import net.kyori.adventure.identity.Identity; @@ -63,8 +66,16 @@ class BungeeCordMessageResponder { ByteBuf buf = Unpooled.buffer(); ByteBufDataOutput out = new ByteBufDataOutput(buf); 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); } @@ -203,8 +214,15 @@ class BungeeCordMessageResponder { out.writeUTF("IPOther"); out.writeUTF(player.getUsername()); - 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); }); @@ -217,8 +235,15 @@ class BungeeCordMessageResponder { out.writeUTF("ServerIP"); out.writeUTF(info.getServerInfo().getName()); - out.writeUTF(info.getServerInfo().getAddress().getHostString()); - out.writeShort(info.getServerInfo().getAddress().getPort()); + SocketAddress address = info.getServerInfo().getAddress(); + 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); }); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 80e6bd7ce..4dfdf9291 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -21,6 +21,7 @@ import com.velocitypowered.proxy.util.except.QuietRuntimeException; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.CompletableFuture; @@ -128,13 +129,18 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } } - private static String cleanRemoteAddress(InetSocketAddress address) { - String addressString = address.getAddress().getHostAddress(); - int ipv6ScopeIdx = addressString.indexOf('%'); - if (ipv6ScopeIdx == -1) { - return addressString; + private static String cleanRemoteAddress(SocketAddress address) { + if (address instanceof InetSocketAddress) { + String addressString = ((InetSocketAddress) address).getAddress().getHostAddress(); + int ipv6ScopeIdx = addressString.indexOf('%'); + if (ipv6ScopeIdx == -1) { + return addressString; + } else { + return addressString.substring(0, ipv6ScopeIdx); + } } else { - return addressString.substring(0, ipv6ScopeIdx); + // Fake it + return "127.0.0.1"; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index fe1144157..c39e46136 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -28,6 +28,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -70,9 +71,10 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, CompletableFuture result = new CompletableFuture<>(); // Note: we use the event loop for the connection the player is on. This reduces context // switches. - server.createBootstrap(proxyPlayer.getConnection().eventLoop()) + SocketAddress destinationAddress = registeredServer.getServerInfo().getAddress(); + server.createBootstrap(proxyPlayer.getConnection().eventLoop(), destinationAddress) .handler(server.getBackendChannelInitializer()) - .connect(registeredServer.getServerInfo().getAddress()) + .connect(destinationAddress) .addListener((ChannelFutureListener) future -> { if (future.isSuccess()) { connection = new MinecraftConnection(future.channel(), server); @@ -95,14 +97,22 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, return result; } + private String getHandshakeRemoteAddress() { + return proxyPlayer.getVirtualHost().map(InetSocketAddress::getHostString).orElse(""); + } + private String createLegacyForwardingAddress(UnaryOperator> propertiesTransform) { // 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 // 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() - .append(registeredServer.getServerInfo().getAddress().getHostString()) + .append(getHandshakeRemoteAddress()) .append('\0') - .append(proxyPlayer.getRemoteAddress().getHostString()) + .append(((InetSocketAddress) proxyPlayer.getRemoteAddress()).getHostString()) .append('\0') .append(proxyPlayer.getGameProfile().getUndashedId()) .append('\0'); @@ -130,7 +140,6 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode(); // Initiate the handshake. - InetSocketAddress destAddress = registeredServer.getServerInfo().getAddress(); ProtocolVersion protocolVersion = proxyPlayer.getConnection().getProtocolVersion(); Handshake handshake = new Handshake(); handshake.setNextStatus(StateRegistry.LOGIN_ID); @@ -141,11 +150,15 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, byte[] secret = server.getConfiguration().getForwardingSecret(); handshake.setServerAddress(createBungeeGuardForwardingAddress(secret)); } else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) { - handshake.setServerAddress(destAddress.getHostString() + HANDSHAKE_HOSTNAME_TOKEN); + handshake.setServerAddress(getHandshakeRemoteAddress() + HANDSHAKE_HOSTNAME_TOKEN); } else { - handshake.setServerAddress(destAddress.getHostString()); + handshake.setServerAddress(getHandshakeRemoteAddress()); + } + + SocketAddress destinationAddr = registeredServer.getServerInfo().getAddress(); + if (destinationAddr instanceof InetSocketAddress) { + handshake.setPort(((InetSocketAddress) destinationAddr).getPort()); } - handshake.setPort(destAddress.getPort()); mc.delayedWrite(handshake); mc.setProtocolVersion(protocolVersion); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 750caf43e..150cfa7b2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -54,6 +54,7 @@ import com.velocitypowered.proxy.util.collect.CappedSet; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -202,8 +203,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { } @Override - public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) connection.getRemoteAddress(); + public SocketAddress getRemoteAddress() { + return connection.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index c5408fc60..1a2ee2ca4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -18,6 +18,7 @@ import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.epoll.EpollChannelOption; import io.netty.util.concurrent.GlobalEventExecutor; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; @@ -34,7 +35,7 @@ public final class ConnectionManager { private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20, 1 << 21); private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class); - private final Map endpoints = new HashMap<>(); + private final Map endpoints = new HashMap<>(); private final TransportType transportType; private final EventLoopGroup bossGroup; private final EventLoopGroup workerGroup; @@ -90,18 +91,20 @@ public final class ConnectionManager { * * @param address the address to bind to */ - public void bind(final InetSocketAddress address) { + public void bind(final SocketAddress address) { final ServerBootstrap bootstrap = new ServerBootstrap() - .channelFactory(this.transportType.serverSocketChannelFactory) + .channelFactory(this.transportType.getServerChannelFactory(address)) .group(this.bossGroup, this.workerGroup) .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK) .childHandler(this.serverChannelInitializer.get()) - .childOption(ChannelOption.TCP_NODELAY, true) - .childOption(ChannelOption.IP_TOS, 0x18) .localAddress(address); - if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) { - bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3); + if (address instanceof InetSocketAddress) { + 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() @@ -142,15 +145,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 target the address the client will connect to * @return a new {@link Bootstrap} */ - public Bootstrap createWorker(@Nullable EventLoopGroup group) { + public Bootstrap createWorker(@Nullable EventLoopGroup group, SocketAddress target) { Bootstrap bootstrap = new Bootstrap() - .channelFactory(this.transportType.socketChannelFactory) + .channelFactory(this.transportType.getClientChannelFactory(target)) .option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.server.getConfiguration().getConnectTimeout()) @@ -167,7 +170,7 @@ public final class ConnectionManager { * * @param oldBind the endpoint to close */ - public void close(InetSocketAddress oldBind) { + public void close(SocketAddress oldBind) { Channel serverChannel = endpoints.remove(oldBind); Preconditions.checkState(serverChannel != null, "Endpoint %s not registered", oldBind); LOGGER.info("Closing endpoint {}", serverChannel.localAddress()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java index d241267b9..7eda99473 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/TransportType.java @@ -1,11 +1,15 @@ package com.velocitypowered.proxy.network; import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory; +import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; +import io.netty.channel.ServerChannel; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollDatagramChannel; +import io.netty.channel.epoll.EpollDomainSocketChannel; import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerDomainSocketChannel; import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.nio.NioEventLoopGroup; @@ -15,6 +19,10 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; 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.function.BiFunction; @@ -22,27 +30,37 @@ enum TransportType { NIO("NIO", NioServerSocketChannel::new, NioSocketChannel::new, NioDatagramChannel::new, + null, + null, (name, type) -> new NioEventLoopGroup(0, createThreadFactory(name, type))), EPOLL("epoll", EpollServerSocketChannel::new, EpollSocketChannel::new, EpollDatagramChannel::new, + EpollServerDomainSocketChannel::new, + EpollDomainSocketChannel::new, (name, type) -> new EpollEventLoopGroup(0, createThreadFactory(name, type))); final String name; final ChannelFactory serverSocketChannelFactory; final ChannelFactory socketChannelFactory; final ChannelFactory datagramChannelFactory; + final ChannelFactory domainServerSocketChannelFactory; + final ChannelFactory domainSocketChannelFactory; final BiFunction eventLoopGroupFactory; TransportType(final String name, final ChannelFactory serverSocketChannelFactory, final ChannelFactory socketChannelFactory, final ChannelFactory datagramChannelFactory, + final ChannelFactory domainServerSocketChannelFactory, + final ChannelFactory domainSocketChannelFactory, final BiFunction eventLoopGroupFactory) { this.name = name; this.serverSocketChannelFactory = serverSocketChannelFactory; this.socketChannelFactory = socketChannelFactory; this.datagramChannelFactory = datagramChannelFactory; + this.domainServerSocketChannelFactory = domainServerSocketChannelFactory; + this.domainSocketChannelFactory = domainSocketChannelFactory; this.eventLoopGroupFactory = eventLoopGroupFactory; } @@ -51,6 +69,28 @@ enum TransportType { return this.name; } + public ChannelFactory 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; + } + + public ChannelFactory 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) { return this.eventLoopGroupFactory.apply(this.name, type); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java index d6b9a78ab..00a3e3fe7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/GS4QueryHandler.java @@ -18,6 +18,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.DatagramPacket; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.ArrayList; @@ -70,8 +71,8 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler .map(server.getConfiguration().getQueryMap()) .currentPlayers(server.getPlayerCount()) .maxPlayers(server.getConfiguration().getShowMaxPlayers()) - .proxyPort(server.getConfiguration().getBind().getPort()) - .proxyHost(server.getConfiguration().getBind().getHostString()) + .proxyPort(((InetSocketAddress) server.getConfiguration().getBind()).getPort()) + .proxyHost(((InetSocketAddress) server.getConfiguration().getBind()).getHostString()) .players(server.getAllPlayers().stream().map(Player::getUsername) .collect(Collectors.toList())) .proxyVersion("Velocity") diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java index fb4110423..cdc1e2555 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java @@ -11,6 +11,8 @@ import com.velocitypowered.proxy.protocol.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.StatusRequest; import com.velocitypowered.proxy.protocol.packet.StatusResponse; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; public class PingSessionHandler implements MinecraftSessionHandler { @@ -33,8 +35,16 @@ public class PingSessionHandler implements MinecraftSessionHandler { public void activated() { Handshake handshake = new Handshake(); 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); connection.delayedWrite(handshake); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java index bec7dab41..5976c841f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java @@ -79,7 +79,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud throw new IllegalStateException("No Velocity proxy instance available"); } CompletableFuture pingFuture = new CompletableFuture<>(); - server.createBootstrap(loop) + server.createBootstrap(loop, serverInfo.getAddress()) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java index b4feb268b..8889d76ed 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/AddressUtil.java @@ -2,8 +2,11 @@ package com.velocitypowered.proxy.util; import com.google.common.base.Preconditions; 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.InetSocketAddress; +import java.net.SocketAddress; import java.net.URI; public final class AddressUtil { @@ -12,14 +15,16 @@ public final class AddressUtil { } /** - * Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned - * {@link InetSocketAddress} is not resolved. + * Attempts to parse a socket address of the form {@code 127.0.0.1:25565}. The returned + * {@link SocketAddress} is not resolved if it is a {@link InetSocketAddress}. * * @param ip the IP to parse * @return the parsed address */ - public static InetSocketAddress parseAddress(String ip) { - Preconditions.checkNotNull(ip, "ip"); + public static SocketAddress parseAddress(String ip) { + if (ip.startsWith("unix://") && Epoll.isAvailable()) { + return new DomainSocketAddress(ip.substring("unix://".length())); + } URI uri = URI.create("tcp://" + ip); try { InetAddress ia = InetAddresses.forUriString(uri.getHost()); @@ -30,13 +35,16 @@ public final class AddressUtil { } /** - * Attempts to parse an IP address of the form {@code 127.0.0.1:25565}. The returned - * {@link InetSocketAddress} is resolved. + * Attempts to parse a socket address of the form {@code 127.0.0.1:25565}. The returned + * {@link SocketAddress} is resolved if it is a {@link InetSocketAddress}. * * @param ip the IP to parse * @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"); URI uri = URI.create("tcp://" + ip); return new InetSocketAddress(uri.getHost(), uri.getPort());