From 80100053236c94fd1dd9502eb051629e2eda0120 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 14 Jun 2019 20:36:26 +0200 Subject: [PATCH] Added ability to firewall (TCP/UDP) connections and to specify connection types (NOTHING, COMPRESS, COMPRESS_AND_ENCRYPT) --- src/dorkbox/network/Client.java | 7 +- src/dorkbox/network/Server.java | 48 +++++- src/dorkbox/network/connection/EndPoint.java | 8 +- .../network/connection/EndPointServer.java | 88 +++++++++- .../network/connection/Shutdownable.java | 1 - .../connectionType/ConnectionRule.java | 163 ++++++++++++++++++ .../connectionType/ConnectionType.java | 35 ++++ .../connectionType/IpConnectionTypeRule.java | 23 +++ .../registration/RegistrationHandler.java | 6 +- .../local/RegistrationLocalHandler.java | 19 +- .../local/RegistrationLocalHandlerClient.java | 21 ++- .../local/RegistrationLocalHandlerServer.java | 21 ++- .../remote/RegistrationRemoteHandler.java | 17 +- .../RegistrationRemoteHandlerClient.java | 6 +- .../RegistrationRemoteHandlerClientTCP.java | 6 +- .../RegistrationRemoteHandlerClientUDP.java | 6 +- .../RegistrationRemoteHandlerServer.java | 28 ++- .../RegistrationRemoteHandlerServerTCP.java | 11 +- .../RegistrationRemoteHandlerServerUDP.java | 18 +- 19 files changed, 460 insertions(+), 72 deletions(-) create mode 100644 src/dorkbox/network/connection/connectionType/ConnectionRule.java create mode 100644 src/dorkbox/network/connection/connectionType/ConnectionType.java create mode 100644 src/dorkbox/network/connection/connectionType/IpConnectionTypeRule.java diff --git a/src/dorkbox/network/Client.java b/src/dorkbox/network/Client.java index 569fb084..9ced23a9 100644 --- a/src/dorkbox/network/Client.java +++ b/src/dorkbox/network/Client.java @@ -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 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 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 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 diff --git a/src/dorkbox/network/Server.java b/src/dorkbox/network/Server.java index b7c6d22e..1582b84f 100644 --- a/src/dorkbox/network/Server.java +++ b/src/dorkbox/network/Server.java @@ -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 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 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 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 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 extends EndPointServer { } } + /** + * Adds an IP+subnet rule that defines if that IP+subnet is allowed/denied connectivity to this server. + *

+ * If there are any IP+subnet added to this list - then ONLY those are permitted (all else are denied) + *

+ * 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 diff --git a/src/dorkbox/network/connection/EndPoint.java b/src/dorkbox/network/connection/EndPoint.java index 25ae2574..6e6ea4dc 100644 --- a/src/dorkbox/network/connection/EndPoint.java +++ b/src/dorkbox/network/connection/EndPoint.java @@ -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! diff --git a/src/dorkbox/network/connection/EndPointServer.java b/src/dorkbox/network/connection/EndPointServer.java index 3e8943a5..9677e735 100644 --- a/src/dorkbox/network/connection/EndPointServer.java +++ b/src/dorkbox/network/connection/EndPointServer.java @@ -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 ipFilterRules = new CopyOnWriteArrayList<>(); + + /** + * Maintains a thread-safe collection of rules used to define the connection type with this server. + */ + protected final CopyOnWriteArrayList 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(); } } diff --git a/src/dorkbox/network/connection/Shutdownable.java b/src/dorkbox/network/connection/Shutdownable.java index 90da4a02..e91b63dc 100644 --- a/src/dorkbox/network/connection/Shutdownable.java +++ b/src/dorkbox/network/connection/Shutdownable.java @@ -425,7 +425,6 @@ class Shutdownable { synchronized (shutdownInProgress) { shutdownChannelsPre(); shutdownAllChannels(); - shutdownEventLoops(); logger.info("Stopping endpoint."); diff --git a/src/dorkbox/network/connection/connectionType/ConnectionRule.java b/src/dorkbox/network/connection/connectionType/ConnectionRule.java new file mode 100644 index 00000000..64567d53 --- /dev/null +++ b/src/dorkbox/network/connection/connectionType/ConnectionRule.java @@ -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); + } + } +} diff --git a/src/dorkbox/network/connection/connectionType/ConnectionType.java b/src/dorkbox/network/connection/connectionType/ConnectionType.java new file mode 100644 index 00000000..4d208a1e --- /dev/null +++ b/src/dorkbox/network/connection/connectionType/ConnectionType.java @@ -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; + } +} diff --git a/src/dorkbox/network/connection/connectionType/IpConnectionTypeRule.java b/src/dorkbox/network/connection/connectionType/IpConnectionTypeRule.java new file mode 100644 index 00000000..72c3f1e4 --- /dev/null +++ b/src/dorkbox/network/connection/connectionType/IpConnectionTypeRule.java @@ -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(); +} diff --git a/src/dorkbox/network/connection/registration/RegistrationHandler.java b/src/dorkbox/network/connection/registration/RegistrationHandler.java index 0aa774d0..8fc24be7 100644 --- a/src/dorkbox/network/connection/registration/RegistrationHandler.java +++ b/src/dorkbox/network/connection/registration/RegistrationHandler.java @@ -24,10 +24,10 @@ import io.netty.channel.EventLoopGroup; @Sharable public abstract -class RegistrationHandler extends ChannelInboundHandlerAdapter { +class RegistrationHandler 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); diff --git a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandler.java b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandler.java index 432dd1fd..b12a146b 100644 --- a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandler.java +++ b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandler.java @@ -23,28 +23,13 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.util.AttributeKey; public abstract -class RegistrationLocalHandler extends RegistrationHandler { +class RegistrationLocalHandler extends RegistrationHandler { public static final AttributeKey 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 { diff --git a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerClient.java b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerClient.java index 9462a6ad..5351eb26 100644 --- a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerClient.java +++ b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerClient.java @@ -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 { 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 */ diff --git a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerServer.java b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerServer.java index 8ee213e1..78cdd56e 100644 --- a/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerServer.java +++ b/src/dorkbox/network/connection/registration/local/RegistrationLocalHandlerServer.java @@ -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 { 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) */ diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandler.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandler.java index 9207bdbb..a0e2f219 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandler.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandler.java @@ -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 extends RegistrationHandler { static final AttributeKey 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); diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClient.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClient.java index 27f1a5d0..9cee44ef 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClient.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClient.java @@ -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 { - 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 diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientTCP.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientTCP.java index 2d5c4f5f..f0ad1051 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientTCP.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientTCP.java @@ -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); diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientUDP.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientUDP.java index 294210b8..4044ec40 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientUDP.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerClientUDP.java @@ -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); } diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServer.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServer.java index c462cf1e..72f0c0d7 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServer.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServer.java @@ -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 { 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 " ==> ") */ diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerTCP.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerTCP.java index cc973876..965351ab 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerTCP.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerTCP.java @@ -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); } diff --git a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerUDP.java b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerUDP.java index 134e75a4..82301fe3 100644 --- a/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerUDP.java +++ b/src/dorkbox/network/connection/registration/remote/RegistrationRemoteHandlerServerUDP.java @@ -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); }