Add support for epoll

This commit is contained in:
kashike
2018-07-26 21:52:04 -07:00
parent bf2835da02
commit 92344083a2
9 changed files with 238 additions and 111 deletions

View File

@@ -2,8 +2,10 @@ package com.velocitypowered.proxy;
public class Velocity {
public static void main(String... args) throws InterruptedException {
VelocityServer server = new VelocityServer();
server.initialize();
final VelocityServer server = VelocityServer.getServer();
server.start();
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown, "Shutdown thread"));
Thread.currentThread().join();
}

View File

@@ -1,99 +1,53 @@
package com.velocitypowered.proxy;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.network.ConnectionManager;
import com.velocitypowered.proxy.connection.http.NettyHttpClient;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils;
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
import com.velocitypowered.proxy.protocol.packets.EncryptionRequest;
import com.velocitypowered.proxy.util.EncryptionUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import net.kyori.text.Component;
import net.kyori.text.serializer.GsonComponentSerializer;
import java.net.InetSocketAddress;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
public class VelocityServer {
private static final VelocityServer INSTANCE = new VelocityServer();
public static final Gson GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
.create();
private static VelocityServer server;
private EventLoopGroup bossGroup;
private EventLoopGroup childGroup;
private final ConnectionManager cm = new ConnectionManager();
private NettyHttpClient httpClient;
private KeyPair serverKeyPair;
public VelocityServer() {
private VelocityServer() {
}
public static VelocityServer getServer() {
return server;
return INSTANCE;
}
public KeyPair getServerKeyPair() {
return serverKeyPair;
}
public void initialize() {
public void start() {
// Create a key pair
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(1024);
serverKeyPair = generator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to generate server encryption key", e);
}
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
// Start the listener
bossGroup = new NioEventLoopGroup(0, new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Netty Boss Thread").build());
childGroup = new NioEventLoopGroup(0, new ThreadFactoryBuilder().setDaemon(true).setNameFormat("Netty I/O Thread #%d").build());
httpClient = new NettyHttpClient(this);
server = this;
new ServerBootstrap()
.channel(NioServerSocketChannel.class)
.group(bossGroup, childGroup)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.IP_TOS, 0x18)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
MinecraftPipelineUtils.strapPipelineForProxy(ch);
MinecraftConnection connection = new MinecraftConnection(ch);
connection.setState(StateRegistry.HANDSHAKE);
connection.setSessionHandler(new HandshakeSessionHandler(connection));
ch.pipeline().addLast("handler", connection);
}
})
.bind(26671)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("Listening on " + future.channel().localAddress());
} else {
System.out.println("Can't bind to " + future.channel().localAddress());
future.cause().printStackTrace();
}
}
});
this.cm.bind(new InetSocketAddress(26671));
}
public Bootstrap initializeGenericBootstrap() {
return new Bootstrap()
.channel(NioSocketChannel.class)
.group(childGroup);
return this.cm.createWorker();
}
public void shutdown() {
this.cm.shutdown();
}
public NettyHttpClient getHttpClient() {

View File

@@ -20,7 +20,14 @@ import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import static com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils.*;
import static com.velocitypowered.network.Connections.CIPHER_DECODER;
import static com.velocitypowered.network.Connections.CIPHER_ENCODER;
import static com.velocitypowered.network.Connections.COMPRESSION_DECODER;
import static com.velocitypowered.network.Connections.COMPRESSION_ENCODER;
import static com.velocitypowered.network.Connections.FRAME_DECODER;
import static com.velocitypowered.network.Connections.FRAME_ENCODER;
import static com.velocitypowered.network.Connections.MINECRAFT_DECODER;
import static com.velocitypowered.network.Connections.MINECRAFT_ENCODER;
/**
* A utility class to make working with the pipeline a little less painful and transparently handles certain Minecraft
@@ -149,8 +156,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
public void setCompressionThreshold(int threshold) {
if (threshold == -1) {
channel.pipeline().remove("compress-decoder");
channel.pipeline().remove("compress-encoder");
channel.pipeline().remove(COMPRESSION_DECODER);
channel.pipeline().remove(COMPRESSION_ENCODER);
return;
}
@@ -158,8 +165,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
MinecraftCompressEncoder encoder = new MinecraftCompressEncoder(threshold, compressor);
MinecraftCompressDecoder decoder = new MinecraftCompressDecoder(threshold, compressor);
channel.pipeline().addBefore(MINECRAFT_DECODER, "compress-decoder", decoder);
channel.pipeline().addBefore(MINECRAFT_ENCODER, "compress-encoder", encoder);
channel.pipeline().addBefore(MINECRAFT_DECODER, COMPRESSION_DECODER, decoder);
channel.pipeline().addBefore(MINECRAFT_ENCODER, COMPRESSION_ENCODER, encoder);
}
public void enableEncryption(byte[] secret) throws GeneralSecurityException {
@@ -167,7 +174,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
VelocityCipher decryptionCipher = new JavaVelocityCipher(false, key);
VelocityCipher encryptionCipher = new JavaVelocityCipher(true, key);
channel.pipeline().addBefore(FRAME_DECODER, "cipher-decoder", new MinecraftCipherDecoder(decryptionCipher));
channel.pipeline().addBefore(FRAME_ENCODER, "cipher-encoder", new MinecraftCipherEncoder(encryptionCipher));
channel.pipeline().addBefore(FRAME_DECODER, CIPHER_DECODER, new MinecraftCipherDecoder(decryptionCipher));
channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher));
}
}

View File

@@ -1,14 +1,29 @@
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
import com.velocitypowered.proxy.protocol.packets.Handshake;
import com.velocitypowered.proxy.protocol.packets.ServerLogin;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.data.ServerInfo;
import com.velocitypowered.proxy.protocol.netty.MinecraftPipelineUtils;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import io.netty.channel.*;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.util.concurrent.TimeUnit;
import static com.velocitypowered.network.Connections.FRAME_DECODER;
import static com.velocitypowered.network.Connections.FRAME_ENCODER;
import static com.velocitypowered.network.Connections.HANDLER;
import static com.velocitypowered.network.Connections.MINECRAFT_DECODER;
import static com.velocitypowered.network.Connections.MINECRAFT_ENCODER;
import static com.velocitypowered.network.Connections.READ_TIMEOUT;
import static com.velocitypowered.network.Connections.SERVER_READ_TIMEOUT_SECONDS;
public class ServerConnection {
private final ServerInfo serverInfo;
@@ -27,12 +42,17 @@ public class ServerConnection {
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
MinecraftPipelineUtils.strapPipelineForBackend(ch);
ch.pipeline()
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(SERVER_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS))
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE)
.addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.TO_CLIENT))
.addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolConstants.Direction.TO_SERVER));
MinecraftConnection connection = new MinecraftConnection(ch);
connection.setState(StateRegistry.HANDSHAKE);
connection.setSessionHandler(new LoginSessionHandler(ServerConnection.this));
ch.pipeline().addLast("handler", connection);
ch.pipeline().addLast(HANDLER, connection);
}
})
.connect(serverInfo.getAddress())

View File

@@ -1,37 +0,0 @@
package com.velocitypowered.proxy.protocol.netty;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import io.netty.channel.Channel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.util.concurrent.TimeUnit;
public interface MinecraftPipelineUtils {
String FRAME_DECODER = "frame-decoder";
String FRAME_ENCODER = "frame-encoder";
String LEGACY_PING_DECODER = "legacy-ping-decoder";
String LEGACY_PING_ENCODER = "legacy-ping-encoder";
String MINECRAFT_DECODER = "minecraft-decoder";
String MINECRAFT_ENCODER = "minecraft-encoder";
String READ_TIMEOUT = "read-timeout";
static void strapPipelineForProxy(Channel ch) {
ch.pipeline()
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast(LEGACY_PING_DECODER, new LegacyPingDecoder())
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE)
.addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE)
.addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.TO_SERVER))
.addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolConstants.Direction.TO_CLIENT));
}
static void strapPipelineForBackend(Channel ch) {
ch.pipeline()
.addLast(READ_TIMEOUT, new ReadTimeoutHandler(30, TimeUnit.SECONDS))
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE)
.addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolConstants.Direction.TO_CLIENT))
.addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolConstants.Direction.TO_SERVER));
}
}

View File

@@ -4,7 +4,19 @@ import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
public enum EncryptionUtils { ;
public enum EncryptionUtils {
;
public static KeyPair createRsaKeyPair(final int keysize) {
try {
final KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(keysize);
return generator.generateKeyPair();
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to generate RSA keypair", e);
}
}
public static String twosComplementSha1Digest(byte[] digest) {
return new BigInteger(digest).toString(16);
}