Added ability to firewall (TCP/UDP) connections and to specify connection types (NOTHING, COMPRESS, COMPRESS_AND_ENCRYPT)

This commit is contained in:
nathan 2019-06-14 20:36:26 +02:00
parent 78d42e985a
commit 8010005323
19 changed files with 460 additions and 72 deletions

View File

@ -24,6 +24,7 @@ import dorkbox.network.connection.BootstrapWrapper;
import dorkbox.network.connection.Connection;
import dorkbox.network.connection.EndPoint;
import dorkbox.network.connection.EndPointClient;
import dorkbox.network.connection.RegistrationWrapperClient;
import dorkbox.network.connection.idle.IdleBridge;
import dorkbox.network.connection.idle.IdleSender;
import dorkbox.network.connection.registration.local.RegistrationLocalHandlerClient;
@ -150,7 +151,7 @@ class Client<C extends Connection> extends EndPointClient implements Connection
localBootstrap.group(newEventLoop(LOCAL, 1, threadName + "-JVM-BOSS"))
.channel(LocalChannel.class)
.remoteAddress(new LocalAddress(config.localChannelName))
.handler(new RegistrationLocalHandlerClient(threadName, registrationWrapper));
.handler(new RegistrationLocalHandlerClient(threadName, (RegistrationWrapperClient) registrationWrapper));
}
@ -183,7 +184,7 @@ class Client<C extends Connection> extends EndPointClient implements Connection
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH))
.remoteAddress(config.host, config.tcpPort)
.handler(new RegistrationRemoteHandlerClientTCP(threadName, registrationWrapper, workerEventLoop));
.handler(new RegistrationRemoteHandlerClientTCP(threadName, (RegistrationWrapperClient) registrationWrapper, workerEventLoop));
// android screws up on this!!
tcpBootstrap.option(ChannelOption.TCP_NODELAY, !OS.isAndroid())
@ -217,7 +218,7 @@ class Client<C extends Connection> extends EndPointClient implements Connection
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH))
.localAddress(new InetSocketAddress(0)) // bind to wildcard
.remoteAddress(new InetSocketAddress(config.host, config.udpPort))
.handler(new RegistrationRemoteHandlerClientUDP(threadName, registrationWrapper, workerEventLoop));
.handler(new RegistrationRemoteHandlerClientUDP(threadName, (RegistrationWrapperClient) registrationWrapper, workerEventLoop));
// Enable to READ and WRITE MULTICAST data (ie, 192.168.1.0)
// in order to WRITE: write as normal, just make sure it ends in .255

View File

@ -19,11 +19,14 @@ import static dorkbox.network.pipeline.ConnectionType.LOCAL;
import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import dorkbox.network.connection.Connection;
import dorkbox.network.connection.EndPoint;
import dorkbox.network.connection.EndPointServer;
import dorkbox.network.connection.RegistrationWrapperServer;
import dorkbox.network.connection.connectionType.ConnectionRule;
import dorkbox.network.connection.registration.local.RegistrationLocalHandlerServer;
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerServerTCP;
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerServerUDP;
@ -49,6 +52,10 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import io.netty.handler.ipfilter.IpFilterRule;
import io.netty.handler.ipfilter.IpFilterRuleType;
import io.netty.handler.ipfilter.IpSubnetFilterRule;
import io.netty.util.NetUtil;
/**
* The server can only be accessed in an ASYNC manner. This means that the server can only be used in RESPONSE to events. If you access the
@ -59,6 +66,10 @@ import io.netty.channel.socket.oio.OioServerSocketChannel;
public
class Server<C extends Connection> extends EndPointServer {
/**
* Rule that will always allow LOCALHOST to connect to the server. This is not added by default
*/
public static final IpFilterRule permitLocalHostRule = new IpSubnetFilterRule(NetUtil.LOCALHOST, 32, IpFilterRuleType.ACCEPT);
/**
* Gets the version number.
@ -158,7 +169,7 @@ class Server<C extends Connection> extends EndPointServer {
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(WRITE_BUFF_LOW, WRITE_BUFF_HIGH))
.localAddress(new LocalAddress(localChannelName))
.childHandler(new RegistrationLocalHandlerServer(threadName, registrationWrapper));
.childHandler(new RegistrationLocalHandlerServer(threadName, (RegistrationWrapperServer) registrationWrapper));
}
@ -195,7 +206,7 @@ class Server<C extends Connection> extends EndPointServer {
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new RegistrationRemoteHandlerServerTCP(threadName, registrationWrapper, workerEventLoop));
.childHandler(new RegistrationRemoteHandlerServerTCP(threadName, (RegistrationWrapperServer) registrationWrapper, workerEventLoop));
// have to check options.host for "0.0.0.0". we don't bind to "0.0.0.0", we bind to "null" to get the "any" address!
if (hostName.equals("0.0.0.0")) {
@ -244,7 +255,7 @@ class Server<C extends Connection> extends EndPointServer {
// TODO: move broadcast to it's own handler, and have UDP server be able to be bound to a specific IP
// OF NOTE: At the end in my case I decided to bind to .255 broadcast address on Linux systems. (to receive broadcast packets)
.localAddress(udpPort) // if you bind to a specific interface, Linux will be unable to receive broadcast packets! see: http://developerweb.net/viewtopic.php?id=5722
.childHandler(new RegistrationRemoteHandlerServerUDP(threadName, registrationWrapper, workerEventLoop));
.childHandler(new RegistrationRemoteHandlerServerUDP(threadName, (RegistrationWrapperServer) registrationWrapper, workerEventLoop));
// // have to check options.host for null. we don't bind to 0.0.0.0, we bind to "null" to get the "any" address!
// if (hostName.equals("0.0.0.0")) {
@ -365,6 +376,37 @@ class Server<C extends Connection> extends EndPointServer {
}
}
/**
* Adds an IP+subnet rule that defines if that IP+subnet is allowed/denied connectivity to this server.
* <p>
* If there are any IP+subnet added to this list - then ONLY those are permitted (all else are denied)
* <p>
* If there is nothing added to this list - then ALL are permitted
*/
public
void addIpFilter(IpFilterRule... rules) {
ipFilterRules.addAll(Arrays.asList(rules));
}
/**
* Adds an IP+subnet rule that defines what type of connection this IP+subnet should have.
* - NOTHING : Nothing happens to the in/out bytes
* - COMPRESS: The in/out bytes are compressed with LZ4-fast
* - COMPRESS_AND_ENCRYPT: The in/out bytes are compressed (LZ4-fast) THEN encrypted (AES-256-GCM)
*
* If no rules are defined, then for LOOPBACK, it will always be `COMPRESS` and for everything else it will always be `COMPRESS_AND_ENCRYPT`.
*
* If rules are defined, then everything by default is `COMPRESS_AND_ENCRYPT`.
*
* The compression algorithm is LZ4-fast, so there is a small performance impact for a very large gain
* Compress : 6.210 micros/op; 629.0 MB/s (output: 55.4%)
* Uncompress : 0.641 micros/op; 6097.9 MB/s
*/
public
void addConnectionTypeFilter(ConnectionRule... rules) {
connectionRules.addAll(Arrays.asList(rules));
}
// called when we are stopped/shut down
@Override
protected

View File

@ -29,6 +29,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dorkbox.network.Configuration;
import dorkbox.network.Server;
import dorkbox.network.connection.bridge.ConnectionBridgeBase;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.wrapper.ChannelLocalWrapper;
@ -214,8 +215,11 @@ class EndPoint extends Shutdownable {
// The registration wrapper permits the registration process to access protected/package fields/methods, that we don't want
// to expose to external code. "this" escaping can be ignored, because it is benign.
//noinspection ThisEscapedInObjectConstruction
registrationWrapper = new RegistrationWrapper(this,
logger);
if (type == Server.class) {
registrationWrapper = new RegistrationWrapperServer(this, logger);
} else {
registrationWrapper = new RegistrationWrapperClient(this, logger);
}
// we have to be able to specify WHAT property store we want to use, since it can change!

View File

@ -15,13 +15,19 @@
*/
package dorkbox.network.connection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.CopyOnWriteArrayList;
import dorkbox.network.Configuration;
import dorkbox.network.Server;
import dorkbox.network.connection.bridge.ConnectionBridgeServer;
import dorkbox.network.connection.registration.UpgradeType;
import dorkbox.network.connection.connectionType.ConnectionRule;
import dorkbox.network.connection.connectionType.ConnectionType;
import dorkbox.util.exceptions.SecurityException;
import io.netty.handler.ipfilter.IpFilterRule;
import io.netty.handler.ipfilter.IpFilterRuleType;
import io.netty.util.NetUtil;
/**
* This serves the purpose of making sure that specific methods are not available to the end user.
@ -29,6 +35,16 @@ import dorkbox.util.exceptions.SecurityException;
public
class EndPointServer extends EndPoint {
/**
* Maintains a thread-safe collection of rules to allow/deny connectivity to this server.
*/
protected final CopyOnWriteArrayList<IpFilterRule> ipFilterRules = new CopyOnWriteArrayList<>();
/**
* Maintains a thread-safe collection of rules used to define the connection type with this server.
*/
protected final CopyOnWriteArrayList<ConnectionRule> connectionRules = new CopyOnWriteArrayList<>();
public
EndPointServer(final Configuration config) throws SecurityException {
super(Server.class, config);
@ -108,8 +124,74 @@ class EndPointServer extends EndPoint {
connectionManager.removeConnection(connection);
}
@Override
protected
void shutdownChannelsPre() {
// Sometimes there might be "lingering" connections (ie, halfway though registration) that need to be closed.
registrationWrapper.clearSessions();
// this calls connectionManager.stop()
super.shutdownChannelsPre();
}
// if no rules, then always yes
// if rules, then default no unless a rule says yes. ACCEPT rules take precedence over REJECT (so if you have both rules, ACCEPT will happen)
boolean acceptRemoteConnection(final InetSocketAddress remoteAddress) {
int size = ipFilterRules.size();
if (size == 0) {
return true;
}
InetAddress address = remoteAddress.getAddress();
// it's possible for a remote address to match MORE than 1 rule.
boolean isAllowed = false;
for (int i = 0; i < size; i++) {
final IpFilterRule rule = ipFilterRules.get(i);
if (rule == null) {
continue;
}
if (isAllowed) {
break;
}
if (rule.matches(remoteAddress)) {
isAllowed = rule.ruleType() == IpFilterRuleType.ACCEPT;
}
}
logger.debug("Validating {} Connection allowed: {}", address, isAllowed);
return isAllowed;
}
// after the handshake, what sort of connection do we want (NONE, COMPRESS, ENCRYPT+COMPRESS)
byte getConnectionUpgradeType(final InetSocketAddress remoteAddress) {
// TODO, crypto/compression based on ip/range of remote address
return UpgradeType.ENCRYPT;
InetAddress address = remoteAddress.getAddress();
int size = connectionRules.size();
// if it's unknown, then by default we encrypt the traffic
ConnectionType connectionType = ConnectionType.COMPRESS_AND_ENCRYPT;
if (size == 0 && address.equals(NetUtil.LOCALHOST)) {
// if nothing is specified, then by default localhost is compression and everything else is encrypted
connectionType = ConnectionType.COMPRESS;
}
for (int i = 0; i < size; i++) {
final ConnectionRule rule = connectionRules.get(i);
if (rule == null) {
continue;
}
if (rule.matches(remoteAddress)) {
connectionType = rule.ruleType();
break;
}
}
logger.debug("Validating {} Permitted type is: {}", remoteAddress, connectionType);
return connectionType.getType();
}
}

View File

@ -425,7 +425,6 @@ class Shutdownable {
synchronized (shutdownInProgress) {
shutdownChannelsPre();
shutdownAllChannels();
shutdownEventLoops();
logger.info("Stopping endpoint.");

View File

@ -0,0 +1,163 @@
package dorkbox.network.connection.connectionType;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import io.netty.util.internal.SocketUtils;
/**
*
*/
public
class ConnectionRule {
private final IpConnectionTypeRule filterRule;
public
ConnectionRule(String ipAddress, int cidrPrefix, ConnectionType ruleType) {
try {
filterRule = selectFilterRule(SocketUtils.addressByName(ipAddress), cidrPrefix, ruleType);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("ipAddress", e);
}
}
public
ConnectionRule(InetAddress ipAddress, int cidrPrefix, ConnectionType ruleType) {
filterRule = selectFilterRule(ipAddress, cidrPrefix, ruleType);
}
public boolean matches(InetSocketAddress remoteAddress) {
return filterRule.matches(remoteAddress);
}
public
ConnectionType ruleType() {
return filterRule.ruleType();
}
private static
IpConnectionTypeRule selectFilterRule(InetAddress ipAddress, int cidrPrefix, ConnectionType ruleType) {
if (ipAddress == null) {
throw new NullPointerException("ipAddress");
}
if (ruleType == null) {
throw new NullPointerException("ruleType");
}
if (ipAddress instanceof Inet4Address) {
return new Ip4SubnetFilterRule((Inet4Address) ipAddress, cidrPrefix, ruleType);
} else if (ipAddress instanceof Inet6Address) {
return new Ip6SubnetFilterRule((Inet6Address) ipAddress, cidrPrefix, ruleType);
} else {
throw new IllegalArgumentException("Only IPv4 and IPv6 addresses are supported");
}
}
private static final class Ip4SubnetFilterRule implements IpConnectionTypeRule {
private final int networkAddress;
private final int subnetMask;
private final ConnectionType ruleType;
private Ip4SubnetFilterRule(Inet4Address ipAddress, int cidrPrefix, ConnectionType ruleType) {
if (cidrPrefix < 0 || cidrPrefix > 32) {
throw new IllegalArgumentException(String.format("IPv4 requires the subnet prefix to be in range of " +
"[0,32]. The prefix was: %d", cidrPrefix));
}
subnetMask = prefixToSubnetMask(cidrPrefix);
networkAddress = ipToInt(ipAddress) & subnetMask;
this.ruleType = ruleType;
}
@Override
public boolean matches(InetSocketAddress remoteAddress) {
final InetAddress inetAddress = remoteAddress.getAddress();
if (inetAddress instanceof Inet4Address) {
int ipAddress = ipToInt((Inet4Address) inetAddress);
return (ipAddress & subnetMask) == networkAddress;
}
return false;
}
@Override
public ConnectionType ruleType() {
return ruleType;
}
private static int ipToInt(Inet4Address ipAddress) {
byte[] octets = ipAddress.getAddress();
assert octets.length == 4;
return (octets[0] & 0xff) << 24 |
(octets[1] & 0xff) << 16 |
(octets[2] & 0xff) << 8 |
octets[3] & 0xff;
}
private static int prefixToSubnetMask(int cidrPrefix) {
/**
* Perform the shift on a long and downcast it to int afterwards.
* This is necessary to handle a cidrPrefix of zero correctly.
* The left shift operator on an int only uses the five least
* significant bits of the right-hand operand. Thus -1 << 32 evaluates
* to -1 instead of 0. The left shift operator applied on a long
* uses the six least significant bits.
*
* Also see https://github.com/netty/netty/issues/2767
*/
return (int) ((-1L << 32 - cidrPrefix) & 0xffffffff);
}
}
private static final class Ip6SubnetFilterRule implements IpConnectionTypeRule {
private static final BigInteger MINUS_ONE = BigInteger.valueOf(-1);
private final BigInteger networkAddress;
private final BigInteger subnetMask;
private final ConnectionType ruleType;
private Ip6SubnetFilterRule(Inet6Address ipAddress, int cidrPrefix, ConnectionType ruleType) {
if (cidrPrefix < 0 || cidrPrefix > 128) {
throw new IllegalArgumentException(String.format("IPv6 requires the subnet prefix to be in range of " +
"[0,128]. The prefix was: %d", cidrPrefix));
}
subnetMask = prefixToSubnetMask(cidrPrefix);
networkAddress = ipToInt(ipAddress).and(subnetMask);
this.ruleType = ruleType;
}
@Override
public boolean matches(InetSocketAddress remoteAddress) {
final InetAddress inetAddress = remoteAddress.getAddress();
if (inetAddress instanceof Inet6Address) {
BigInteger ipAddress = ipToInt((Inet6Address) inetAddress);
return ipAddress.and(subnetMask).equals(networkAddress);
}
return false;
}
@Override
public ConnectionType ruleType() {
return ruleType;
}
private static BigInteger ipToInt(Inet6Address ipAddress) {
byte[] octets = ipAddress.getAddress();
assert octets.length == 16;
return new BigInteger(octets);
}
private static BigInteger prefixToSubnetMask(int cidrPrefix) {
return MINUS_ONE.shiftLeft(128 - cidrPrefix);
}
}
}

View File

@ -0,0 +1,35 @@
package dorkbox.network.connection.connectionType;
import dorkbox.network.connection.registration.UpgradeType;
/**
* Used in {@link IpConnectionTypeRule} to decide what kind of connection a matching IP Address should have.
*/
public enum ConnectionType {
/**
* No compression, no encryption
*/
NOTHING(UpgradeType.NONE),
/**
* Only compression
*/
COMPRESS(UpgradeType.COMPRESS),
/**
* Compression + encryption
*/
COMPRESS_AND_ENCRYPT(UpgradeType.ENCRYPT)
;
private final byte type;
ConnectionType(byte type) {
this.type = type;
}
public
byte getType() {
return type;
}
}

View File

@ -0,0 +1,23 @@
package dorkbox.network.connection.connectionType;
import java.net.InetSocketAddress;
import io.netty.handler.ipfilter.IpFilterRuleType;
/**
* Implement this interface to create new rules.
*/
public interface IpConnectionTypeRule {
/**
* @return This method should return true if remoteAddress is valid according to your criteria. False otherwise.
*/
boolean matches(InetSocketAddress remoteAddress);
/**
* @return This method should return {@link IpFilterRuleType#ACCEPT} if all
* {@link IpConnectionTypeRule#matches(InetSocketAddress)} for which {@link #matches(InetSocketAddress)}
* returns true should the accepted. If you want to exclude all of those IP addresses then
* {@link IpFilterRuleType#REJECT} should be returned.
*/
ConnectionType ruleType();
}

View File

@ -24,10 +24,10 @@ import io.netty.channel.EventLoopGroup;
@Sharable
public abstract
class RegistrationHandler extends ChannelInboundHandlerAdapter {
class RegistrationHandler<T extends RegistrationWrapper> extends ChannelInboundHandlerAdapter {
protected static final String CONNECTION_HANDLER = "connection";
protected final RegistrationWrapper registrationWrapper;
protected final T registrationWrapper;
protected final org.slf4j.Logger logger;
protected final String name;
protected final EventLoopGroup workerEventLoop;
@ -38,7 +38,7 @@ class RegistrationHandler extends ChannelInboundHandlerAdapter {
* @param workerEventLoop can be null for local JVM connections
*/
public
RegistrationHandler(final String name, RegistrationWrapper registrationWrapper, final EventLoopGroup workerEventLoop) {
RegistrationHandler(final String name, T registrationWrapper, final EventLoopGroup workerEventLoop) {
this.name = name;
this.workerEventLoop = workerEventLoop;
this.logger = org.slf4j.LoggerFactory.getLogger(this.name);

View File

@ -23,28 +23,13 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
public abstract
class RegistrationLocalHandler extends RegistrationHandler {
class RegistrationLocalHandler<T extends RegistrationWrapper> extends RegistrationHandler<T> {
public static final AttributeKey<MetaChannel> META_CHANNEL = AttributeKey.valueOf(RegistrationLocalHandler.class, "MetaChannel.local");
RegistrationLocalHandler(String name, RegistrationWrapper registrationWrapper) {
RegistrationLocalHandler(String name, T registrationWrapper) {
super(name, registrationWrapper, null);
}
/**
* STEP 1: Channel is first created
*/
@Override
protected
void initChannel(Channel channel) {
MetaChannel metaChannel = registrationWrapper.createSessionServer();
metaChannel.localChannel = channel;
channel.attr(META_CHANNEL)
.set(metaChannel);
logger.trace("New LOCAL connection.");
}
@Override
public
void channelActive(final ChannelHandlerContext context) throws Exception {

View File

@ -16,7 +16,7 @@
package dorkbox.network.connection.registration.local;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperClient;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -25,13 +25,28 @@ import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
public
class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
class RegistrationLocalHandlerClient extends RegistrationLocalHandler<RegistrationWrapperClient> {
public
RegistrationLocalHandlerClient(String name, RegistrationWrapper registrationWrapper) {
RegistrationLocalHandlerClient(String name, RegistrationWrapperClient registrationWrapper) {
super(name, registrationWrapper);
}
/**
* STEP 1: Channel is first created
*/
@Override
protected
void initChannel(Channel channel) {
MetaChannel metaChannel = registrationWrapper.createSession(0);
metaChannel.localChannel = channel;
channel.attr(META_CHANNEL)
.set(metaChannel);
logger.trace("New LOCAL connection.");
}
/**
* STEP 2: Channel is now active. Start the registration process
*/

View File

@ -16,8 +16,8 @@
package dorkbox.network.connection.registration.local;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapper.STATE;
import dorkbox.network.connection.RegistrationWrapperServer;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -26,14 +26,29 @@ import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
public
class RegistrationLocalHandlerServer extends RegistrationLocalHandler {
class RegistrationLocalHandlerServer extends RegistrationLocalHandler<RegistrationWrapperServer> {
public
RegistrationLocalHandlerServer(String name, RegistrationWrapper registrationWrapper) {
RegistrationLocalHandlerServer(String name, RegistrationWrapperServer registrationWrapper) {
super(name, registrationWrapper);
}
/**
* STEP 1: Channel is first created
*/
@Override
protected
void initChannel(Channel channel) {
MetaChannel metaChannel = registrationWrapper.createSession();
metaChannel.localChannel = channel;
channel.attr(META_CHANNEL)
.set(metaChannel);
logger.trace("New LOCAL connection.");
}
/**
* STEP 2: Channel is now active. Start the registration process (Client starts the process)
*/

View File

@ -34,7 +34,6 @@ import dorkbox.network.pipeline.tcp.KryoDecoderTcp;
import dorkbox.network.pipeline.tcp.KryoDecoderTcpCompression;
import dorkbox.network.pipeline.tcp.KryoDecoderTcpCrypto;
import dorkbox.network.pipeline.tcp.KryoDecoderTcpNone;
import dorkbox.network.serialization.NetworkSerializationManager;
import dorkbox.util.crypto.CryptoECC;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
@ -51,7 +50,7 @@ import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.GenericFutureListener;
public abstract
class RegistrationRemoteHandler extends RegistrationHandler {
class RegistrationRemoteHandler<T extends RegistrationWrapper> extends RegistrationHandler<T> {
static final AttributeKey<LinkedList> MESSAGES = AttributeKey.valueOf(RegistrationRemoteHandler.class, "messages");
static final String DELETE_IP = "eleteIP"; // purposefully missing the "D", since that is a system parameter, which starts with "-D"
@ -86,12 +85,8 @@ class RegistrationRemoteHandler extends RegistrationHandler {
protected final NetworkSerializationManager serializationManager;
RegistrationRemoteHandler(final String name, final RegistrationWrapper registrationWrapper, final EventLoopGroup workerEventLoop) {
RegistrationRemoteHandler(final String name, final T registrationWrapper, final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
this.serializationManager = registrationWrapper.getSerialization();
}
/**
@ -112,7 +107,7 @@ class RegistrationRemoteHandler extends RegistrationHandler {
///////////////////////
// DECODE (or upstream)
///////////////////////
pipeline.addFirst(TCP_DECODE, new KryoDecoderTcp(this.serializationManager)); // cannot be shared because of possible fragmentation.
pipeline.addFirst(TCP_DECODE, new KryoDecoderTcp(registrationWrapper.getSerialization())); // cannot be shared because of possible fragmentation.
}
else if (isUdpChannel) {
// can be shared because there cannot be fragmentation for our UDP packets. If there is, we throw an error and continue...
@ -225,15 +220,15 @@ class RegistrationRemoteHandler extends RegistrationHandler {
// cannot be shared because of possible fragmentation.
switch (upgradeType) {
case (UpgradeType.NONE) :
pipeline.replace(TCP_DECODE, TCP_DECODE_NONE, new KryoDecoderTcpNone(this.serializationManager));
pipeline.replace(TCP_DECODE, TCP_DECODE_NONE, new KryoDecoderTcpNone(registrationWrapper.getSerialization()));
break;
case (UpgradeType.COMPRESS) :
pipeline.replace(TCP_DECODE, TCP_DECODE_COMPRESS, new KryoDecoderTcpCompression(this.serializationManager));
pipeline.replace(TCP_DECODE, TCP_DECODE_COMPRESS, new KryoDecoderTcpCompression(registrationWrapper.getSerialization()));
break;
case (UpgradeType.ENCRYPT) :
pipeline.replace(TCP_DECODE, TCP_DECODE_CRYPTO, new KryoDecoderTcpCrypto(this.serializationManager));
pipeline.replace(TCP_DECODE, TCP_DECODE_CRYPTO, new KryoDecoderTcpCrypto(registrationWrapper.getSerialization()));
break;
default:
throw new IllegalArgumentException("Unable to upgrade TCP connection pipeline for type: " + upgradeType);

View File

@ -33,7 +33,7 @@ import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperClient;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import dorkbox.util.crypto.CryptoECC;
@ -44,9 +44,9 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
public
class RegistrationRemoteHandlerClient extends RegistrationRemoteHandler {
class RegistrationRemoteHandlerClient extends RegistrationRemoteHandler<RegistrationWrapperClient> {
RegistrationRemoteHandlerClient(final String name, final RegistrationWrapper registrationWrapper, final EventLoopGroup workerEventLoop) {
RegistrationRemoteHandlerClient(final String name, final RegistrationWrapperClient registrationWrapper, final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
// check to see if we need to delete an IP address as commanded from the user prompt

View File

@ -15,7 +15,7 @@
*/
package dorkbox.network.connection.registration.remote;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperClient;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -26,7 +26,7 @@ public
class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandlerClient {
public
RegistrationRemoteHandlerClientTCP(final String name,
final RegistrationWrapper registrationWrapper,
final RegistrationWrapperClient registrationWrapper,
final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
}
@ -70,7 +70,7 @@ class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandlerClient
// TCP channel registration is ALWAYS first, so this is the correct way to do this.
if (metaChannel == null) {
metaChannel = registrationWrapper.createSessionClient(sessionId);
metaChannel = registrationWrapper.createSession(sessionId);
metaChannel.tcpChannel = channel;
logger.debug("New TCP connection. Saving meta-channel id: {}", metaChannel.sessionId);

View File

@ -18,7 +18,7 @@ package dorkbox.network.connection.registration.remote;
import java.io.IOException;
import java.net.InetSocketAddress;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperClient;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -30,7 +30,7 @@ public
class RegistrationRemoteHandlerClientUDP extends RegistrationRemoteHandlerClient {
public
RegistrationRemoteHandlerClientUDP(final String name,
final RegistrationWrapper registrationWrapper,
final RegistrationWrapperClient registrationWrapper,
final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
}
@ -91,7 +91,7 @@ class RegistrationRemoteHandlerClientUDP extends RegistrationRemoteHandlerClient
metaChannel = registrationWrapper.getSession(sessionId);
if (metaChannel == null) {
metaChannel = registrationWrapper.createSessionClient(sessionId);
metaChannel = registrationWrapper.createSession(sessionId);
logger.debug("New UDP connection. Saving meta-channel id: {}", metaChannel.sessionId);
}

View File

@ -35,8 +35,9 @@ import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.EndPoint;
import dorkbox.network.connection.RegistrationWrapper.STATE;
import dorkbox.network.connection.RegistrationWrapperServer;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import dorkbox.util.crypto.CryptoECC;
@ -47,7 +48,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
public
class RegistrationRemoteHandlerServer extends RegistrationRemoteHandler {
class RegistrationRemoteHandlerServer extends RegistrationRemoteHandler<RegistrationWrapperServer> {
private static final long ECDH_TIMEOUT = TimeUnit.MINUTES.toNanos(10L); // 10 minutes in nanoseconds
private static final ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(CryptoECC.curve25519);
@ -56,10 +57,31 @@ class RegistrationRemoteHandlerServer extends RegistrationRemoteHandler {
private volatile long ecdhTimeout = System.nanoTime();
RegistrationRemoteHandlerServer(final String name, final RegistrationWrapper registrationWrapper, final EventLoopGroup workerEventLoop) {
RegistrationRemoteHandlerServer(final String name, final RegistrationWrapperServer registrationWrapper, final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
}
/**
* STEP 1: Channel is first created
*/
@Override
protected
void initChannel(final Channel channel) {
// check to see if this connection is permitted.
final InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
if (!registrationWrapper.acceptRemoteConnection(remoteAddress)) {
StringBuilder stringBuilder = new StringBuilder();
EndPoint.getHostDetails(stringBuilder, remoteAddress);
logger.error("Remote connection [{}] is not permitted! Aborting connection process.", stringBuilder.toString());
shutdown(channel, 0);
return;
}
super.initChannel(channel);
}
/**
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
*/

View File

@ -16,7 +16,7 @@
package dorkbox.network.connection.registration.remote;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperServer;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -28,11 +28,13 @@ class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandlerServer
public
RegistrationRemoteHandlerServerTCP(final String name,
final RegistrationWrapper registrationWrapper,
final RegistrationWrapperServer registrationWrapper,
final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
}
/**
* STEP 3-XXXXX: We pass registration messages around until we the registration handshake is complete!
*/
@ -49,7 +51,7 @@ class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandlerServer
MetaChannel metaChannel;
int sessionId = registration.sessionID;
if (sessionId == 0) {
metaChannel = registrationWrapper.createSessionServer();
metaChannel = registrationWrapper.createSession();
metaChannel.tcpChannel = channel;
// TODO: use this: channel.voidPromise();
logger.debug("New TCP connection. Saving meta-channel id: {}", metaChannel.sessionId);
@ -70,8 +72,7 @@ class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandlerServer
logger.error("Error registering TCP with remote client!");
// this is what happens when the registration happens too quickly...
Object connection = context.pipeline()
.last();
Object connection = context.pipeline().last();
if (connection instanceof ConnectionImpl) {
((ConnectionImpl) connection).channelRead(context, message);
}

View File

@ -16,7 +16,7 @@
package dorkbox.network.connection.registration.remote;
import dorkbox.network.connection.ConnectionImpl;
import dorkbox.network.connection.RegistrationWrapper;
import dorkbox.network.connection.RegistrationWrapperServer;
import dorkbox.network.connection.registration.MetaChannel;
import dorkbox.network.connection.registration.Registration;
import io.netty.channel.Channel;
@ -30,11 +30,14 @@ public
class RegistrationRemoteHandlerServerUDP extends RegistrationRemoteHandlerServer {
public
RegistrationRemoteHandlerServerUDP(final String name,
final RegistrationWrapper registrationWrapper,
final RegistrationWrapperServer registrationWrapper,
final EventLoopGroup workerEventLoop) {
super(name, registrationWrapper, workerEventLoop);
}
/**
* STEP 3-XXXXX: We pass registration messages around until we the registration handshake is complete!
*/
@Override
public
void channelRead(final ChannelHandlerContext context, Object message) throws Exception {
@ -47,7 +50,7 @@ class RegistrationRemoteHandlerServerUDP extends RegistrationRemoteHandlerServer
int sessionId = 0;
sessionId = registration.sessionID;
if (sessionId == 0) {
metaChannel = registrationWrapper.createSessionServer();
metaChannel = registrationWrapper.createSession();
metaChannel.udpChannel = channel;
logger.debug("New UDP connection. Saving meta-channel id: {}", metaChannel.sessionId);
}
@ -66,12 +69,15 @@ class RegistrationRemoteHandlerServerUDP extends RegistrationRemoteHandlerServer
readServer(context, channel, registration, "UDP server", metaChannel);
}
else {
else if (message instanceof io.netty.channel.socket.DatagramPacket) {
logger.error("Error registering UDP with remote client!");
shutdown(channel, 0);
}
else {
logger.error("Error registering UDP with remote client! Attempting to queue message: " + message.getClass());
// this is what happens when the registration happens too quickly...
Object connection = context.pipeline()
.last();
Object connection = context.pipeline().last();
if (connection instanceof ConnectionImpl) {
((ConnectionImpl) connection).channelRead(context, message);
}