You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2017/08/28 16:34:24 UTC

[1/3] mina-sshd git commit: [SSHD-766] Separate forwarding filter functionality according to sshd-config options

Repository: mina-sshd
Updated Branches:
  refs/heads/master 306bef267 -> aa551bc0e


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
index 6c94581..0f76fef 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/ForwardingFilter.java
@@ -18,13 +18,7 @@
  */
 package org.apache.sshd.server.forward;
 
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
@@ -32,149 +26,41 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
  *
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public interface ForwardingFilter {
-    /**
-     * <p>
-     * Determine if the session may arrange for agent forwarding.
-     * </p>
-     *
-     * <p>
-     * This server process will open a new listen socket locally and export
-     * the address in the {@link org.apache.sshd.agent.SshAgent#SSH_AUTHSOCKET_ENV_NAME} environment
-     * variable.
-     * </p>
-     *
-     * @param session The {@link Session} requesting permission to forward the agent.
-     * @param requestType The request type string that triggered this call
-     * @return true if the agent forwarding is permitted, false if denied.
-     */
-    boolean canForwardAgent(Session session, String requestType);
-
-    /**
-     * <p>
-     * Determine if the session may arrange for X11 forwarding.
-     * </p>
-     *
-     * <p>
-     * This server process will open a new listen socket locally and export
-     * the address in the environment so X11 clients can be tunneled to the
-     * user's X11 display server.
-     * </p>
-     *
-     * @param session The {@link Session} requesting permission to forward X11 connections.
-     * @param requestType The request type string that triggered this call
-     * @return true if the X11 forwarding is permitted, false if denied.
-     */
-    boolean canForwardX11(Session session, String requestType);
-
+public interface ForwardingFilter extends AgentForwardingFilter, X11ForwardingFilter, TcpForwardingFilter {
     /**
-     * <p>
-     * Determine if the session may listen for inbound connections.
-     * </p>
-     *
-     * <p>
-     * This server process will open a new listen socket on the address given
-     * by the client (usually 127.0.0.1 but may be any address).  Any inbound
-     * connections to this socket will be tunneled over the session to the
-     * client, which the client will then forward the connection to another
-     * host on the client's side of the network.
-     * </p>
+     * Wraps separate filtering policies into one - any {@code null} one is assumed to be disabled
      *
-     * @param address address the client has requested this server listen
-     *                for inbound connections on, and relay them through the client.
-     * @param session The {@link Session} requesting permission to listen for connections.
-     * @return true if the socket is permitted; false if it must be denied.
+     * @param agentFilter The {@link AgentForwardingFilter}
+     * @param x11Filter The {@link X11ForwardingFilter}
+     * @param tcpFilter The {@link TcpForwardingFilter}
+     * @return The combined implementation
      */
-    boolean canListen(SshdSocketAddress address, Session session);
-
-    /**
-     * The type of requested connection forwarding. The type's {@link #getName()}
-     * method returns the SSH request type
-     */
-    enum Type implements NamedResource {
-        Direct("direct-tcpip"),
-        Forwarded("forwarded-tcpip");
-
-        public static final Set<Type> VALUES =
-                Collections.unmodifiableSet(EnumSet.allOf(Type.class));
-
-        private final String name;
-
-        Type(String name) {
-            this.name = name;
-        }
-
-        @Override
-        public final String getName() {
-            return name;
+    static ForwardingFilter asForwardingFilter(
+            AgentForwardingFilter agentFilter, X11ForwardingFilter x11Filter, TcpForwardingFilter tcpFilter) {
+        if ((agentFilter == null) && (x11Filter == null) && (tcpFilter == null)) {
+            return RejectAllForwardingFilter.INSTANCE;
         }
 
-        /**
-         * @param name Either the enum name or the request - ignored if {@code null}/empty
-         * @return The matching {@link Type} value - case <U>insensitive</U>,
-         * or {@code null} if no match found
-         * @see #fromName(String)
-         * @see #fromEnumName(String)
-         */
-        public static Type fromString(String name) {
-            if (GenericUtils.isEmpty(name)) {
-                return null;
+        return new ForwardingFilter() {
+            @Override
+            public boolean canForwardAgent(Session session, String requestType) {
+                return (agentFilter != null) && agentFilter.canForwardAgent(session, requestType);
             }
 
-            Type t = fromName(name);
-            if (t == null) {
-                t = fromEnumName(name);
+            @Override
+            public boolean canForwardX11(Session session, String requestType) {
+                return (x11Filter != null) && x11Filter.canForwardX11(session, requestType);
             }
 
-            return t;
-        }
-
-        /**
-         * @param name The request name - ignored if {@code null}/empty
-         * @return The matching {@link Type} value - case <U>insensitive</U>,
-         * or {@code null} if no match found
-         */
-        public static Type fromName(String name) {
-            return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
-        }
-
-        /**
-         * @param name The enum value name - ignored if {@code null}/empty
-         * @return The matching {@link Type} value - case <U>insensitive</U>,
-         * or {@code null} if no match found
-         */
-        public static Type fromEnumName(String name) {
-            if (GenericUtils.isEmpty(name)) {
-                return null;
+            @Override
+            public boolean canListen(SshdSocketAddress address, Session session) {
+                return (tcpFilter != null) && tcpFilter.canListen(address, session);
             }
 
-            for (Type t : VALUES) {
-                if (name.equalsIgnoreCase(t.name())) {
-                    return t;
-                }
+            @Override
+            public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+                return (tcpFilter != null) && tcpFilter.canConnect(type, address, session);
             }
-
-            return null;
-        }
+        };
     }
-
-    /**
-     * <p>
-     * Determine if the session may create an outbound connection.
-     * </p>
-     *
-     * <p>
-     * This server process will connect to another server listening on the
-     * address specified by the client.  Usually this is to another port on
-     * the same host (127.0.0.1) but may be to any other system this server
-     * can reach on the server's side of the network.
-     * </p>
-     *
-     * @param type    The {@link Type} of requested connection forwarding
-     * @param address address the client has requested this server listen
-     *                for inbound connections on, and relay them through the client.
-     * @param session session requesting permission to listen for connections.
-     * @return true if the socket is permitted; false if it must be denied.
-     */
-    boolean canConnect(Type type, SshdSocketAddress address, Session session);
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpForwardingFilter.java
new file mode 100644
index 0000000..715190a
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpForwardingFilter.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.forward;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface TcpForwardingFilter {
+    // According to http://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+    TcpForwardingFilter DEFAULT = new TcpForwardingFilter() {
+        @Override
+        public boolean canListen(SshdSocketAddress address, Session session) {
+            return true;
+        }
+
+        @Override
+        public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return TcpForwardingFilter.class.getSimpleName() + "[DEFAULT]";
+        }
+    };
+
+    /**
+     * <p>
+     * Determine if the session may listen for inbound connections.
+     * </p>
+     *
+     * <p>
+     * This server process will open a new listen socket on the address given
+     * by the client (usually 127.0.0.1 but may be any address).  Any inbound
+     * connections to this socket will be tunneled over the session to the
+     * client, which the client will then forward the connection to another
+     * host on the client's side of the network.
+     * </p>
+     *
+     * @param address address the client has requested this server listen
+     * for inbound connections on, and relay them through the client.
+     * @param session The {@link Session} requesting permission to listen for connections.
+     * @return true if the socket is permitted; false if it must be denied.
+     */
+    boolean canListen(SshdSocketAddress address, Session session);
+
+    /**
+     * The type of requested connection forwarding. The type's {@link #getName()}
+     * method returns the SSH request type
+     */
+    enum Type implements NamedResource {
+        Direct("direct-tcpip"),
+        Forwarded("forwarded-tcpip");
+
+        public static final Set<Type> VALUES =
+                Collections.unmodifiableSet(EnumSet.allOf(Type.class));
+
+        private final String name;
+
+        Type(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public final String getName() {
+            return name;
+        }
+
+        /**
+         * @param name Either the enum name or the request - ignored if {@code null}/empty
+         * @return The matching {@link Type} value - case <U>insensitive</U>,
+         * or {@code null} if no match found
+         * @see #fromName(String)
+         * @see #fromEnumName(String)
+         */
+        public static Type fromString(String name) {
+            if (GenericUtils.isEmpty(name)) {
+                return null;
+            }
+
+            Type t = fromName(name);
+            if (t == null) {
+                t = fromEnumName(name);
+            }
+
+            return t;
+        }
+
+        /**
+         * @param name The request name - ignored if {@code null}/empty
+         * @return The matching {@link Type} value - case <U>insensitive</U>,
+         * or {@code null} if no match found
+         */
+        public static Type fromName(String name) {
+            return NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, VALUES);
+        }
+
+        /**
+         * @param name The enum value name - ignored if {@code null}/empty
+         * @return The matching {@link Type} value - case <U>insensitive</U>,
+         * or {@code null} if no match found
+         */
+        public static Type fromEnumName(String name) {
+            if (GenericUtils.isEmpty(name)) {
+                return null;
+            }
+
+            for (Type t : VALUES) {
+                if (name.equalsIgnoreCase(t.name())) {
+                    return t;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * <p>
+     * Determine if the session may create an outbound connection.
+     * </p>
+     *
+     * <p>
+     * This server process will connect to another server listening on the
+     * address specified by the client.  Usually this is to another port on
+     * the same host (127.0.0.1) but may be to any other system this server
+     * can reach on the server's side of the network.
+     * </p>
+     *
+     * @param type    The {@link Type} of requested connection forwarding
+     * @param address address the client has requested this server listen
+     * for inbound connections on, and relay them through the client.
+     * @param session session requesting permission to listen for connections.
+     * @return true if the socket is permitted; false if it must be denied.
+     */
+    boolean canConnect(Type type, SshdSocketAddress address, Session session);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
index 5ddb601..010fa11 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
@@ -122,7 +122,7 @@ public class TcpipServerChannel extends AbstractServerChannel {
                 address = new SshdSocketAddress(hostToConnect, portToConnect);
                 break;
             case Forwarded:
-                address = service.getTcpipForwarder().getForwardedPort(portToConnect);
+                address = service.getForwardingFilter().getForwardedPort(portToConnect);
                 break;
             default:
                 throw new IllegalStateException("Unknown server channel type: " + type);
@@ -130,7 +130,7 @@ public class TcpipServerChannel extends AbstractServerChannel {
 
         Session session = getSession();
         FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        ForwardingFilter filter = manager.getTcpipForwardingFilter();
+        TcpForwardingFilter filter = manager.getTcpForwardingFilter();
         OpenFuture f = new DefaultOpenFuture(this);
         try {
             if ((address == null) || (filter == null) || (!filter.canConnect(type, address, session))) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/forward/X11ForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/X11ForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/X11ForwardingFilter.java
new file mode 100644
index 0000000..b9c6b1a
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/X11ForwardingFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.forward;
+
+import org.apache.sshd.common.session.Session;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface X11ForwardingFilter {
+    // According to https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+    X11ForwardingFilter DEFAULT = (session, request) -> false;
+
+    /**
+     * <p>
+     * Determine if the session may arrange for X11 forwarding.
+     * </p>
+     *
+     * <p>
+     * This server process will open a new listen socket locally and export
+     * the address in the environment so X11 clients can be tunneled to the
+     * user's X11 display server.
+     * </p>
+     *
+     * @param session The {@link Session} requesting permission to forward X11 connections.
+     * @param requestType The request type string that triggered this call
+     * @return true if the X11 forwarding is permitted, false if denied.
+     */
+    boolean canForwardX11(Session session, String requestType);
+
+    static X11ForwardingFilter of(boolean enabled) {
+        return (session, request) -> enabled;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
index 2756174..be42a6e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/CancelTcpipForwardHandler.java
@@ -22,7 +22,7 @@ import java.util.Objects;
 import java.util.function.IntUnaryOperator;
 
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.forward.ForwardingFilter;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.helpers.AbstractConnectionServiceRequestHandler;
@@ -62,7 +62,7 @@ public class CancelTcpipForwardHandler extends AbstractConnectionServiceRequestH
             log.debug("process({})[{}] {} reply={}", connectionService, request, socketAddress, wantReply);
         }
 
-        TcpipForwarder forwarder = Objects.requireNonNull(connectionService.getTcpipForwarder(), "No TCP/IP forwarder");
+        ForwardingFilter forwarder = Objects.requireNonNull(connectionService.getForwardingFilter(), "No TCP/IP forwarder");
         forwarder.localPortForwardingCancelled(socketAddress);
 
         if (wantReply) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
index 7c6ea38..b0ecbf3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/global/TcpipForwardHandler.java
@@ -22,7 +22,7 @@ import java.util.Objects;
 import java.util.function.IntUnaryOperator;
 
 import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.forward.ForwardingFilter;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.helpers.AbstractConnectionServiceRequestHandler;
@@ -59,7 +59,7 @@ public class TcpipForwardHandler extends AbstractConnectionServiceRequestHandler
         String address = buffer.getString();
         int port = buffer.getInt();
         SshdSocketAddress socketAddress = new SshdSocketAddress(address, port);
-        TcpipForwarder forwarder = Objects.requireNonNull(connectionService.getTcpipForwarder(), "No TCP/IP forwarder");
+        ForwardingFilter forwarder = Objects.requireNonNull(connectionService.getForwardingFilter(), "No TCP/IP forwarder");
         SshdSocketAddress bound = forwarder.localPortForwardingRequested(socketAddress);
         if (log.isDebugEnabled()) {
             log.debug("process({})[{}][want-reply-{}] {} => {}",

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
index 285b7c1..7a1fa72 100644
--- a/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/ProxyTest.java
@@ -128,7 +128,7 @@ public class ProxyTest extends BaseTestSupport {
         sshd = setupTestServer();
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 2048);
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.MAX_PACKET_SIZE, "256");
-        sshd.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         sshd.addPortForwardingEventListener(serverSideListener);
         sshd.start();
         sshPort = sshd.getPort();
@@ -288,7 +288,7 @@ public class ProxyTest extends BaseTestSupport {
         client = setupTestClient();
         PropertyResolverUtils.updateProperty(client, FactoryManager.WINDOW_SIZE, 2048);
         PropertyResolverUtils.updateProperty(client, FactoryManager.MAX_PACKET_SIZE, 256);
-        client.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        client.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         if (listener != null) {
             client.addPortForwardingEventListener(listener);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
index 3442042..eaf3deb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/agent/AgentTest.java
@@ -115,13 +115,13 @@ public class AgentTest extends BaseTestSupport {
         try (SshServer sshd1 = setupTestServer()) {
             sshd1.setShellFactory(shellFactory);
             sshd1.setAgentFactory(agentFactory);
-            sshd1.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+            sshd1.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
             sshd1.start();
 
             final int port1 = sshd1.getPort();
             try (SshServer sshd2 = setupTestServer()) {
                 sshd2.setShellFactory(new TestEchoShellFactory());
-                sshd1.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+                sshd1.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
                 sshd2.setAgentFactory(new ProxyAgentFactory());
                 sshd2.start();
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
index d0cc821..30ce313 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientAuthenticationManagerTest.java
@@ -39,7 +39,7 @@ import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.channel.ChannelListener;
-import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
+import org.apache.sshd.common.forward.DefaultForwarderFactory;
 import org.apache.sshd.common.forward.PortForwardingEventListener;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
@@ -236,7 +236,7 @@ public class ClientAuthenticationManagerTest extends BaseTestSupport {
 
     private ClientSession createMockClientSession() throws Exception {
         ClientFactoryManager client = Mockito.mock(ClientFactoryManager.class);
-        Mockito.when(client.getTcpipForwarderFactory()).thenReturn(DefaultTcpipForwarderFactory.INSTANCE);
+        Mockito.when(client.getForwarderFactory()).thenReturn(DefaultForwarderFactory.INSTANCE);
         Mockito.when(client.getSessionListenerProxy()).thenReturn(new SessionListener() {
             @Override
             public void sessionEvent(Session session, Event event) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerApacheClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerApacheClientTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerApacheClientTest.java
index f6ac886..6d5d5c1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerApacheClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerApacheClientTest.java
@@ -59,7 +59,7 @@ public class ApacheServerApacheClientTest extends AbstractServerCloseTestSupport
         LOG.info("Starting SSHD...");
         server = SshServer.setUpDefaultServer();
         server.setPasswordAuthenticator((u, p, s) -> true);
-        server.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        server.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
         server.start();
         sshServerPort = server.getPort();
@@ -76,7 +76,7 @@ public class ApacheServerApacheClientTest extends AbstractServerCloseTestSupport
     @Before
     public void createClient() throws IOException {
         SshClient client = SshClient.setUpDefaultClient();
-        client.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        client.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         client.start();
         LOG.info("Connecting...");
         session = client.connect("user", TEST_LOCALHOST, sshServerPort).verify(TIMEOUT).getSession();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerJSchClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerJSchClientTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerJSchClientTest.java
index 3f15a91..bec67a1 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerJSchClientTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/ApacheServerJSchClientTest.java
@@ -70,7 +70,7 @@ public class ApacheServerJSchClientTest extends AbstractServerCloseTestSupport {
         LOG.info("Starting SSHD...");
         server = SshServer.setUpDefaultServer();
         server.setPasswordAuthenticator((u, p, s) -> true);
-        server.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        server.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
         server.start();
         sshServerPort = server.getPort();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
index 0184422..dd076ae 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingLoadTest.java
@@ -145,7 +145,7 @@ public class PortForwardingLoadTest extends BaseTestSupport {
     @Before
     public void setUp() throws Exception {
         sshd = setupTestServer();
-        sshd.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         sshd.addPortForwardingEventListener(serverSideListener);
         sshd.start();
         sshPort = sshd.getPort();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
index 0e23bf9..9b88a84 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/forward/PortForwardingTest.java
@@ -153,7 +153,7 @@ public class PortForwardingTest extends BaseTestSupport {
         sshd = Utils.setupTestServer(PortForwardingTest.class);
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.WINDOW_SIZE, 2048);
         PropertyResolverUtils.updateProperty(sshd, FactoryManager.MAX_PACKET_SIZE, 256);
-        sshd.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         sshd.addPortForwardingEventListener(SERVER_SIDE_LISTENER);
         sshd.start();
         sshPort = sshd.getPort();
@@ -162,9 +162,9 @@ public class PortForwardingTest extends BaseTestSupport {
             REQUESTS_QUEUE.clear();
         }
 
-        TcpipForwarderFactory factory = Objects.requireNonNull(sshd.getTcpipForwarderFactory(), "No TcpipForwarderFactory");
-        sshd.setTcpipForwarderFactory(new TcpipForwarderFactory() {
-            private final Class<?>[] interfaces = {TcpipForwarder.class};
+        ForwardingFilterFactory factory = Objects.requireNonNull(sshd.getForwarderFactory(), "No ForwarderFactory");
+        sshd.setForwarderFactory(new ForwardingFilterFactory() {
+            private final Class<?>[] interfaces = {ForwardingFilter.class};
             private final Map<String, String> method2req =
                     GenericUtils.<String, String>mapBuilder(String.CASE_INSENSITIVE_ORDER)
                         .put("localPortForwardingRequested", TcpipForwardHandler.REQUEST)
@@ -172,13 +172,13 @@ public class PortForwardingTest extends BaseTestSupport {
                         .build();
 
             @Override
-            public TcpipForwarder create(ConnectionService service) {
+            public ForwardingFilter create(ConnectionService service) {
                 Thread thread = Thread.currentThread();
                 ClassLoader cl = thread.getContextClassLoader();
 
-                TcpipForwarder forwarder = factory.create(service);
-                return (TcpipForwarder) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
-                    private final org.slf4j.Logger log = LoggerFactory.getLogger(TcpipForwarder.class);
+                ForwardingFilter forwarder = factory.create(service);
+                return (ForwardingFilter) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
+                    private final org.slf4j.Logger log = LoggerFactory.getLogger(ForwardingFilter.class);
 
                     @SuppressWarnings("synthetic-access")
                     @Override
@@ -797,7 +797,7 @@ public class PortForwardingTest extends BaseTestSupport {
     protected ClientSession createNativeSession(PortForwardingEventListener listener) throws Exception {
         PropertyResolverUtils.updateProperty(client, FactoryManager.WINDOW_SIZE, 2048);
         PropertyResolverUtils.updateProperty(client, FactoryManager.MAX_PACKET_SIZE, 256);
-        client.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        client.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         if (listener != null) {
             client.addPortForwardingEventListener(listener);
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
index e67cf23..9320cc7 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/subsystem/sftp/SshFsMounter.java
@@ -317,7 +317,7 @@ public final class SshFsMounter {
 
         sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE);
         sshd.setPasswordAuthenticator(AcceptAllPasswordAuthenticator.INSTANCE);
-        sshd.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
         sshd.setCommandFactory(new ScpCommandFactory.Builder().withDelegate(MounterCommandFactory.INSTANCE).build());
         sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
         sshd.start();


[3/3] mina-sshd git commit: [SSHD-766] Separate forwarding filter functionality according to sshd-config options

Posted by lg...@apache.org.
[SSHD-766] Separate forwarding filter functionality according to sshd-config options


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/aa551bc0
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/aa551bc0
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/aa551bc0

Branch: refs/heads/master
Commit: aa551bc0ed07430ee768e98a57b75cd56f3927e0
Parents: 306bef2
Author: Goldstein Lyor <ly...@c-b4.com>
Authored: Mon Aug 28 15:12:26 2017 +0300
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Mon Aug 28 19:34:56 2017 +0300

----------------------------------------------------------------------
 .../java/org/apache/sshd/client/SshClient.java  |    2 +-
 .../client/session/AbstractClientSession.java   |   24 +-
 .../org/apache/sshd/common/BaseBuilder.java     |   20 +-
 .../org/apache/sshd/common/FactoryManager.java  |   25 +-
 .../common/config/AllowTcpForwardingValue.java  |   63 --
 .../sshd/common/config/SshConfigFileReader.java |   13 -
 .../common/forward/DefaultForwarderFactory.java |   82 ++
 .../common/forward/DefaultForwardingFilter.java | 1003 +++++++++++++++++
 .../common/forward/DefaultTcpipForwarder.java   | 1007 ------------------
 .../forward/DefaultTcpipForwarderFactory.java   |   82 --
 .../sshd/common/forward/ForwardingFilter.java   |   59 +
 .../common/forward/ForwardingFilterFactory.java |   37 +
 .../sshd/common/forward/TcpipForwarder.java     |   54 -
 .../common/forward/TcpipForwarderFactory.java   |   37 -
 .../common/helpers/AbstractFactoryManager.java  |   22 +-
 .../sshd/common/session/ConnectionService.java  |    8 +-
 .../helpers/AbstractConnectionService.java      |   32 +-
 .../java/org/apache/sshd/server/SshServer.java  |   56 +-
 .../sshd/server/channel/ChannelSession.java     |    7 +-
 .../server/config/AllowTcpForwardingValue.java  |  106 ++
 .../config/SshServerConfigFileReader.java       |  111 ++
 .../server/forward/AgentForwardingFilter.java   |   51 +
 .../sshd/server/forward/ForwardingFilter.java   |  162 +--
 .../server/forward/TcpForwardingFilter.java     |  162 +++
 .../sshd/server/forward/TcpipServerChannel.java |    4 +-
 .../server/forward/X11ForwardingFilter.java     |   51 +
 .../global/CancelTcpipForwardHandler.java       |    4 +-
 .../sshd/server/global/TcpipForwardHandler.java |    4 +-
 .../test/java/org/apache/sshd/ProxyTest.java    |    4 +-
 .../java/org/apache/sshd/agent/AgentTest.java   |    4 +-
 .../client/ClientAuthenticationManagerTest.java |    4 +-
 .../forward/ApacheServerApacheClientTest.java   |    4 +-
 .../forward/ApacheServerJSchClientTest.java     |    2 +-
 .../common/forward/PortForwardingLoadTest.java  |    2 +-
 .../sshd/common/forward/PortForwardingTest.java |   18 +-
 .../server/subsystem/sftp/SshFsMounter.java     |    2 +-
 36 files changed, 1808 insertions(+), 1520 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index c8cc1db..e4d37ab 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -378,7 +378,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
     protected void checkConfig() {
         super.checkConfig();
 
-        Objects.requireNonNull(getTcpipForwarderFactory(), "TcpipForwarderFactory not set");
+        Objects.requireNonNull(getForwarderFactory(), "ForwarderFactory not set");
         Objects.requireNonNull(getServerKeyVerifier(), "ServerKeyVerifier not set");
         Objects.requireNonNull(getHostConfigEntryResolver(), "HostConfigEntryResolver not set");
         Objects.requireNonNull(getClientIdentityLoader(), "ClientIdentityLoader not set");

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 9e85843..4e9e6c9 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -58,7 +58,7 @@ import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.CipherNone;
 import org.apache.sshd.common.config.keys.KeyUtils;
-import org.apache.sshd.common.forward.TcpipForwarder;
+import org.apache.sshd.common.forward.ForwardingFilter;
 import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
 import org.apache.sshd.common.future.KeyExchangeFuture;
 import org.apache.sshd.common.io.IoSession;
@@ -388,37 +388,43 @@ public abstract class AbstractClientSession extends AbstractSession implements C
 
     @Override
     public SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
-        return getTcpipForwarder().startLocalPortForwarding(local, remote);
+        ForwardingFilter filter = getForwardingFilter();
+        return filter.startLocalPortForwarding(local, remote);
     }
 
     @Override
     public void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
-        getTcpipForwarder().stopLocalPortForwarding(local);
+        ForwardingFilter filter = getForwardingFilter();
+        filter.stopLocalPortForwarding(local);
     }
 
     @Override
     public SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
-        return getTcpipForwarder().startRemotePortForwarding(remote, local);
+        ForwardingFilter filter = getForwardingFilter();
+        return filter.startRemotePortForwarding(remote, local);
     }
 
     @Override
     public void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
-        getTcpipForwarder().stopRemotePortForwarding(remote);
+        ForwardingFilter filter = getForwardingFilter();
+        filter.stopRemotePortForwarding(remote);
     }
 
     @Override
     public SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        return getTcpipForwarder().startDynamicPortForwarding(local);
+        ForwardingFilter filter = getForwardingFilter();
+        return filter.startDynamicPortForwarding(local);
     }
 
     @Override
     public void stopDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        getTcpipForwarder().stopDynamicPortForwarding(local);
+        ForwardingFilter filter = getForwardingFilter();
+        filter.stopDynamicPortForwarding(local);
     }
 
-    protected TcpipForwarder getTcpipForwarder() {
+    protected ForwardingFilter getForwardingFilter() {
         ConnectionService service = Objects.requireNonNull(getConnectionService(), "No connection service");
-        return Objects.requireNonNull(service.getTcpipForwarder(), "No forwarder");
+        return Objects.requireNonNull(service.getForwardingFilter(), "No forwarder");
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
index 534ac0e..250dc63 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java
@@ -30,8 +30,8 @@ import org.apache.sshd.common.cipher.Cipher;
 import org.apache.sshd.common.compression.Compression;
 import org.apache.sshd.common.file.FileSystemFactory;
 import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
-import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
-import org.apache.sshd.common.forward.TcpipForwarderFactory;
+import org.apache.sshd.common.forward.DefaultForwarderFactory;
+import org.apache.sshd.common.forward.ForwardingFilterFactory;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.KeyExchange;
@@ -59,7 +59,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
 
     public static final ForwardingFilter DEFAULT_FORWARDING_FILTER = RejectAllForwardingFilter.INSTANCE;
 
-    public static final TcpipForwarderFactory DEFAULT_FORWARDER_FACTORY = DefaultTcpipForwarderFactory.INSTANCE;
+    public static final ForwardingFilterFactory DEFAULT_FORWARDER_FACTORY = DefaultForwarderFactory.INSTANCE;
 
     /**
      * The default {@link BuiltinCiphers} setup in order of preference
@@ -138,7 +138,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
     protected Factory<Random> randomFactory;
     protected List<NamedFactory<Channel>> channelFactories;
     protected FileSystemFactory fileSystemFactory;
-    protected TcpipForwarderFactory tcpipForwarderFactory;
+    protected ForwardingFilterFactory forwarderFactory;
     protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
     protected ForwardingFilter forwardingFilter;
 
@@ -171,8 +171,8 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
             forwardingFilter = DEFAULT_FORWARDING_FILTER;
         }
 
-        if (tcpipForwarderFactory == null) {
-            tcpipForwarderFactory = DEFAULT_FORWARDER_FACTORY;
+        if (forwarderFactory == null) {
+            forwarderFactory = DEFAULT_FORWARDER_FACTORY;
         }
 
         return me();
@@ -223,8 +223,8 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
         return me();
     }
 
-    public S tcpipForwarderFactory(TcpipForwarderFactory tcpipForwarderFactory) {
-        this.tcpipForwarderFactory = tcpipForwarderFactory;
+    public S forwarderFactory(ForwardingFilterFactory forwarderFactory) {
+        this.forwarderFactory = forwarderFactory;
         return me();
     }
 
@@ -253,8 +253,8 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
         ssh.setMacFactories(macFactories);
         ssh.setChannelFactories(channelFactories);
         ssh.setFileSystemFactory(fileSystemFactory);
-        ssh.setTcpipForwardingFilter(forwardingFilter);
-        ssh.setTcpipForwarderFactory(tcpipForwarderFactory);
+        ssh.setForwardingFilter(forwardingFilter);
+        ssh.setForwarderFactory(forwarderFactory);
         ssh.setGlobalRequestHandlers(globalRequestHandlers);
         return ssh;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index 8652cd8..71e06da 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -27,15 +27,18 @@ import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.ChannelListenerManager;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.forward.ForwardingFilterFactory;
 import org.apache.sshd.common.forward.PortForwardingEventListenerManager;
-import org.apache.sshd.common.forward.TcpipForwarderFactory;
 import org.apache.sshd.common.io.IoServiceFactory;
 import org.apache.sshd.common.kex.KexFactoryManager;
 import org.apache.sshd.common.random.Random;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.ReservedSessionMessagesManager;
 import org.apache.sshd.common.session.SessionListenerManager;
+import org.apache.sshd.server.forward.AgentForwardingFilter;
 import org.apache.sshd.server.forward.ForwardingFilter;
+import org.apache.sshd.server.forward.TcpForwardingFilter;
+import org.apache.sshd.server.forward.X11ForwardingFilter;
 
 /**
  * This interface allows retrieving all the <code>NamedFactory</code> used
@@ -415,14 +418,26 @@ public interface FactoryManager
      *
      * @return The {@link ForwardingFilter} or {@code null}
      */
-    ForwardingFilter getTcpipForwardingFilter();
+    ForwardingFilter getForwardingFilter();
+
+    default TcpForwardingFilter getTcpForwardingFilter() {
+        return getForwardingFilter();
+    }
+
+    default AgentForwardingFilter getAgentForwardingFilter() {
+        return getForwardingFilter();
+    }
+
+    default X11ForwardingFilter getX11ForwardingFilter() {
+        return getForwardingFilter();
+    }
 
     /**
-     * Retrieve the tcpip forwarder factory used to support tcpip forwarding.
+     * Retrieve the forwarder factory used to support forwarding.
      *
-     * @return The {@link TcpipForwarderFactory}
+     * @return The {@link ForwardingFilterFactory}
      */
-    TcpipForwarderFactory getTcpipForwarderFactory();
+    ForwardingFilterFactory getForwarderFactory();
 
     /**
      * Retrieve the <code>FileSystemFactory</code> to be used to traverse the file system.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java b/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
deleted file mode 100644
index c641f33..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/AllowTcpForwardingValue.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.sshd.common.config;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Set;
-
-import org.apache.sshd.common.util.GenericUtils;
-
-/**
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- * @see <A HREF="http://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5">sshd_config(5) section</A>
- */
-public enum AllowTcpForwardingValue {
-    ALL,
-    NONE,
-    LOCAL,
-    REMOTE;
-
-    public static final Set<AllowTcpForwardingValue> VALUES =
-            Collections.unmodifiableSet(EnumSet.allOf(AllowTcpForwardingValue.class));
-
-    // NOTE: it also interprets "yes" as "all" and "no" as "none"
-    public static AllowTcpForwardingValue fromString(String s) {
-        if (GenericUtils.isEmpty(s)) {
-            return null;
-        }
-
-        if ("yes".equalsIgnoreCase(s)) {
-            return ALL;
-        }
-
-        if ("no".equalsIgnoreCase(s)) {
-            return NONE;
-        }
-
-        for (AllowTcpForwardingValue v : VALUES) {
-            if (s.equalsIgnoreCase(v.name())) {
-                return v;
-            }
-        }
-
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
index 7f6b5e8..b6d87d4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java
@@ -78,21 +78,8 @@ public final class SshConfigFileReader {
 
     public static final char COMMENT_CHAR = '#';
 
-    // Some well known configuration properties names and values
-    public static final String BANNER_CONFIG_PROP = "Banner";
-    public static final String VISUAL_HOST_KEY = "VisualHostKey";
-    public static final String DEFAULT_VISUAL_HOST_KEY = "no";
     public static final String COMPRESSION_PROP = "Compression";
     public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName();
-    public static final String ALLOW_TCP_FORWARDING_CONFIG_PROP = "AllowTcpForwarding";
-    public static final String DEFAULT_TCP_FORWARDING = "yes";
-    public static final boolean DEFAULT_TCP_FORWARDING_VALUE = parseBooleanValue(DEFAULT_TCP_FORWARDING);
-    public static final String ALLOW_AGENT_FORWARDING_CONFIG_PROP = "AllowAgentForwarding";
-    public static final String DEFAULT_AGENT_FORWARDING = "yes";
-    public static final boolean DEFAULT_AGENT_FORWARDING_VALUE = parseBooleanValue(DEFAULT_AGENT_FORWARDING);
-    public static final String ALLOW_X11_FORWARDING_CONFIG_PROP = "X11Forwarding";
-    public static final String DEFAULT_X11_FORWARDING = "yes";
-    public static final boolean DEFAULT_X11_FORWARDING_VALUE = parseBooleanValue(DEFAULT_X11_FORWARDING);
     public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions";
     public static final int DEFAULT_MAX_SESSIONS = 10;
     public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwarderFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwarderFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwarderFactory.java
new file mode 100644
index 0000000..ace0562
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwarderFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.forward;
+
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.util.EventListenerUtils;
+
+/**
+ * The default {@link ForwardingFilterFactory} implementation.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultForwarderFactory implements ForwardingFilterFactory, PortForwardingEventListenerManager {
+    public static final DefaultForwarderFactory INSTANCE = new DefaultForwarderFactory() {
+        @Override
+        public void addPortForwardingEventListener(PortForwardingEventListener listener) {
+            throw new UnsupportedOperationException("addPortForwardingListener(" + listener + ") N/A on default instance");
+        }
+
+        @Override
+        public void removePortForwardingEventListener(PortForwardingEventListener listener) {
+            throw new UnsupportedOperationException("removePortForwardingEventListener(" + listener + ") N/A on default instance");
+        }
+
+        @Override
+        public PortForwardingEventListener getPortForwardingEventListenerProxy() {
+            return PortForwardingEventListener.EMPTY;
+        }
+    };
+
+    private final Collection<PortForwardingEventListener> listeners = new CopyOnWriteArraySet<>();
+    private final PortForwardingEventListener listenerProxy;
+
+    public DefaultForwarderFactory() {
+        listenerProxy = EventListenerUtils.proxyWrapper(PortForwardingEventListener.class, getClass().getClassLoader(), listeners);
+    }
+
+    @Override
+    public PortForwardingEventListener getPortForwardingEventListenerProxy() {
+        return listenerProxy;
+    }
+
+    @Override
+    public void addPortForwardingEventListener(PortForwardingEventListener listener) {
+        listeners.add(PortForwardingEventListener.validateListener(listener));
+    }
+
+    @Override
+    public void removePortForwardingEventListener(PortForwardingEventListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        listeners.remove(PortForwardingEventListener.validateListener(listener));
+    }
+
+    @Override
+    public ForwardingFilter create(ConnectionService service) {
+        ForwardingFilter forwarder = new DefaultForwardingFilter(service);
+        forwarder.addPortForwardingEventListenerManager(this);
+        return forwarder;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
new file mode 100644
index 0000000..64f37db
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultForwardingFilter.java
@@ -0,0 +1,1003 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.forward;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.client.channel.ClientChannelEvent;
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoHandlerFactory;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.session.ConnectionService;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionHolder;
+import org.apache.sshd.common.util.EventListenerUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Invoker;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.closeable.AbstractInnerCloseable;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.server.forward.TcpForwardingFilter;
+
+/**
+ * Requests a &quot;tcpip-forward&quot; action
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class DefaultForwardingFilter
+        extends AbstractInnerCloseable
+        implements ForwardingFilter, SessionHolder<Session>, PortForwardingEventListenerManager {
+
+    /**
+     * Used to configure the timeout (milliseconds) for receiving a response
+     * for the forwarding request
+     *
+     * @see #DEFAULT_FORWARD_REQUEST_TIMEOUT
+     */
+    public static final String FORWARD_REQUEST_TIMEOUT = "tcpip-forward-request-timeout";
+
+    /**
+     * Default value for {@link #FORWARD_REQUEST_TIMEOUT} if none specified
+     */
+    public static final long DEFAULT_FORWARD_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
+
+    public static final Set<ClientChannelEvent> STATIC_IO_MSG_RECEIVED_EVENTS =
+            Collections.unmodifiableSet(EnumSet.of(ClientChannelEvent.OPENED, ClientChannelEvent.CLOSED));
+
+    private final ConnectionService service;
+    private final IoHandlerFactory socksProxyIoHandlerFactory = () -> new SocksProxy(getConnectionService());
+    private final Session sessionInstance;
+    private final Map<Integer, SshdSocketAddress> localToRemote = new TreeMap<>(Comparator.naturalOrder());
+    private final Map<Integer, SshdSocketAddress> remoteToLocal = new TreeMap<>(Comparator.naturalOrder());
+    private final Map<Integer, SocksProxy> dynamicLocal = new TreeMap<>(Comparator.naturalOrder());
+    private final Set<LocalForwardingEntry> localForwards = new HashSet<>();
+    private final IoHandlerFactory staticIoHandlerFactory = StaticIoHandler::new;
+    private final Collection<PortForwardingEventListener> listeners = new CopyOnWriteArraySet<>();
+    private final Collection<PortForwardingEventListenerManager> managersHolder = new CopyOnWriteArraySet<>();
+    private final PortForwardingEventListener listenerProxy;
+
+    private IoAcceptor acceptor;
+
+    public DefaultForwardingFilter(ConnectionService service) {
+        this.service = Objects.requireNonNull(service, "No connection service");
+        this.sessionInstance = Objects.requireNonNull(service.getSession(), "No session");
+        this.listenerProxy = EventListenerUtils.proxyWrapper(PortForwardingEventListener.class, getClass().getClassLoader(), listeners);
+    }
+
+    @Override
+    public PortForwardingEventListener getPortForwardingEventListenerProxy() {
+        return listenerProxy;
+    }
+
+    @Override
+    public void addPortForwardingEventListener(PortForwardingEventListener listener) {
+        listeners.add(PortForwardingEventListener.validateListener(listener));
+    }
+
+    @Override
+    public void removePortForwardingEventListener(PortForwardingEventListener listener) {
+        if (listener == null) {
+            return;
+        }
+
+        listeners.remove(PortForwardingEventListener.validateListener(listener));
+    }
+
+    @Override
+    public Collection<PortForwardingEventListenerManager> getRegisteredManagers() {
+        return managersHolder.isEmpty() ? Collections.emptyList() : new ArrayList<>(managersHolder);
+    }
+
+    @Override
+    public boolean addPortForwardingEventListenerManager(PortForwardingEventListenerManager manager) {
+        return managersHolder.add(Objects.requireNonNull(manager, "No manager"));
+    }
+
+    @Override
+    public boolean removePortForwardingEventListenerManager(PortForwardingEventListenerManager manager) {
+        if (manager == null) {
+            return false;
+        }
+
+        return managersHolder.remove(manager);
+    }
+
+    @Override
+    public Session getSession() {
+        return sessionInstance;
+    }
+
+    public final ConnectionService getConnectionService() {
+        return service;
+    }
+
+    protected Collection<PortForwardingEventListener> getDefaultListeners() {
+        Collection<PortForwardingEventListener> defaultListeners = new ArrayList<>();
+        defaultListeners.add(getPortForwardingEventListenerProxy());
+
+        Session session = getSession();
+        PortForwardingEventListener l = session.getPortForwardingEventListenerProxy();
+        if (l != null) {
+            defaultListeners.add(l);
+        }
+
+        FactoryManager manager = (session == null) ? null : session.getFactoryManager();
+        l = (manager == null) ? null : manager.getPortForwardingEventListenerProxy();
+        if (l != null) {
+            defaultListeners.add(l);
+        }
+
+        return defaultListeners;
+    }
+
+    @Override
+    public synchronized SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
+        Objects.requireNonNull(local, "Local address is null");
+        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
+        Objects.requireNonNull(remote, "Remote address is null");
+
+        if (isClosed()) {
+            throw new IllegalStateException("TcpipForwarder is closed");
+        }
+        if (isClosing()) {
+            throw new IllegalStateException("TcpipForwarder is closing");
+        }
+
+        InetSocketAddress bound;
+        int port;
+        signalEstablishingExplicitTunnel(local, remote, true);
+        try {
+            bound = doBind(local, staticIoHandlerFactory);
+            port = bound.getPort();
+            SshdSocketAddress prev;
+            synchronized (localToRemote) {
+                prev = localToRemote.put(port, remote);
+            }
+
+            if (prev != null) {
+                throw new IOException("Multiple local port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev);
+            }
+        } catch (IOException | RuntimeException e) {
+            try {
+                stopLocalPortForwarding(local);
+            } catch (IOException | RuntimeException err) {
+                e.addSuppressed(err);
+            }
+            signalEstablishedExplicitTunnel(local, remote, true, null, e);
+            throw e;
+        }
+
+        try {
+            SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port);
+            if (log.isDebugEnabled()) {
+                log.debug("startLocalPortForwarding(" + local + " -> " + remote + "): " + result);
+            }
+            signalEstablishedExplicitTunnel(local, remote, true, result, null);
+            return result;
+        } catch (IOException | RuntimeException e) {
+            stopLocalPortForwarding(local);
+            throw e;
+        }
+    }
+
+    @Override
+    public synchronized void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
+        Objects.requireNonNull(local, "Local address is null");
+
+        SshdSocketAddress bound;
+        synchronized (localToRemote) {
+            bound = localToRemote.remove(local.getPort());
+        }
+
+        if ((bound != null) && (acceptor != null)) {
+            if (log.isDebugEnabled()) {
+                log.debug("stopLocalPortForwarding(" + local + ") unbind " + bound);
+            }
+
+            signalTearingDownExplicitTunnel(bound, true);
+            try {
+                acceptor.unbind(bound.toInetSocketAddress());
+            } catch (RuntimeException e) {
+                signalTornDownExplicitTunnel(bound, true, e);
+                throw e;
+            }
+
+            signalTornDownExplicitTunnel(bound, true, null);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("stopLocalPortForwarding(" + local + ") no mapping/acceptor for " + bound);
+            }
+        }
+    }
+
+    @Override
+    public synchronized SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
+        Objects.requireNonNull(local, "Local address is null");
+        Objects.requireNonNull(remote, "Remote address is null");
+
+        String remoteHost = remote.getHostName();
+        int remotePort = remote.getPort();
+        Session session = getSession();
+        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, remoteHost.length() + Long.SIZE);
+        buffer.putString("tcpip-forward");
+        buffer.putBoolean(true);    // want reply
+        buffer.putString(remoteHost);
+        buffer.putInt(remotePort);
+
+        long timeout = session.getLongProperty(FORWARD_REQUEST_TIMEOUT, DEFAULT_FORWARD_REQUEST_TIMEOUT);
+        Buffer result;
+        int port;
+        signalEstablishingExplicitTunnel(local, remote, false);
+        try {
+            result = session.request("tcpip-forward", buffer, timeout, TimeUnit.MILLISECONDS);
+            if (result == null) {
+                throw new SshException("Tcpip forwarding request denied by server");
+            }
+            port = (remotePort == 0) ? result.getInt() : remote.getPort();
+            // TODO: Is it really safe to only store the local address after the request ?
+            SshdSocketAddress prev;
+            synchronized (remoteToLocal) {
+                prev = remoteToLocal.put(port, local);
+            }
+
+            if (prev != null) {
+                throw new IOException("Multiple remote port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev);
+            }
+        } catch (IOException | RuntimeException e) {
+            try {
+                stopRemotePortForwarding(remote);
+            } catch (IOException | RuntimeException err) {
+                e.addSuppressed(err);
+            }
+            signalEstablishedExplicitTunnel(local, remote, false, null, e);
+            throw e;
+        }
+
+        try {
+            SshdSocketAddress bound = new SshdSocketAddress(remoteHost, port);
+            if (log.isDebugEnabled()) {
+                log.debug("startRemotePortForwarding(" + remote + " -> " + local + "): " + bound);
+            }
+
+            signalEstablishedExplicitTunnel(local, remote, false, bound, null);
+            return bound;
+        } catch (IOException | RuntimeException e) {
+            stopRemotePortForwarding(remote);
+            throw e;
+        }
+    }
+
+    @Override
+    public synchronized void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
+        SshdSocketAddress bound;
+        synchronized (remoteToLocal) {
+            bound = remoteToLocal.remove(remote.getPort());
+        }
+
+        if (bound != null) {
+            if (log.isDebugEnabled()) {
+                log.debug("stopRemotePortForwarding(" + remote + ") cancel forwarding to " + bound);
+            }
+
+            String remoteHost = remote.getHostName();
+            Session session = getSession();
+            Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, remoteHost.length() + Long.SIZE);
+            buffer.putString("cancel-tcpip-forward");
+            buffer.putBoolean(false);   // want reply
+            buffer.putString(remoteHost);
+            buffer.putInt(remote.getPort());
+
+            signalTearingDownExplicitTunnel(bound, false);
+            try {
+                session.writePacket(buffer);
+            } catch (IOException | RuntimeException e) {
+                signalTornDownExplicitTunnel(bound, false, e);
+                throw e;
+            }
+
+            signalTornDownExplicitTunnel(bound, false, null);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("stopRemotePortForwarding(" + remote + ") no binding found");
+            }
+        }
+    }
+
+    protected void signalTearingDownExplicitTunnel(SshdSocketAddress boundAddress, boolean localForwarding) throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalTearingDownExplicitTunnel(l, boundAddress, localForwarding);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal tearing down explicit tunnel for local=" + localForwarding
+                        + " on bound=" + boundAddress, t);
+            }
+        }
+    }
+
+    protected void signalTearingDownExplicitTunnel(
+            PortForwardingEventListener listener, SshdSocketAddress boundAddress, boolean localForwarding)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.tearingDownExplicitTunnel(getSession(), boundAddress, localForwarding);
+    }
+
+    protected void signalTornDownExplicitTunnel(SshdSocketAddress boundAddress, boolean localForwarding, Throwable reason) throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalTornDownExplicitTunnel(l, boundAddress, localForwarding, reason);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal torn down explicit tunnel local=" + localForwarding
+                        + " on bound=" + boundAddress, t);
+            }
+        }
+    }
+
+    protected void signalTornDownExplicitTunnel(
+            PortForwardingEventListener listener, SshdSocketAddress boundAddress, boolean localForwarding, Throwable reason)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.tornDownExplicitTunnel(getSession(), boundAddress, localForwarding, reason);
+    }
+
+    @Override
+    public synchronized SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException {
+        Objects.requireNonNull(local, "Local address is null");
+        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
+
+        if (isClosed()) {
+            throw new IllegalStateException("TcpipForwarder is closed");
+        }
+        if (isClosing()) {
+            throw new IllegalStateException("TcpipForwarder is closing");
+        }
+
+        SocksProxy socksProxy = new SocksProxy(service);
+        SocksProxy prev;
+        InetSocketAddress bound;
+        int port;
+        signalEstablishingDynamicTunnel(local);
+        try {
+            bound = doBind(local, socksProxyIoHandlerFactory);
+            port = bound.getPort();
+            synchronized (dynamicLocal) {
+                prev = dynamicLocal.put(port, socksProxy);
+            }
+
+            if (prev != null) {
+                throw new IOException("Multiple dynamic port mappings found for port=" + port + ": current=" + socksProxy + ", previous=" + prev);
+            }
+        } catch (IOException | RuntimeException e) {
+            try {
+                stopDynamicPortForwarding(local);
+            } catch (IOException | RuntimeException err) {
+                e.addSuppressed(err);
+            }
+            signalEstablishedDynamicTunnel(local, null, e);
+            throw e;
+        }
+
+        try {
+            SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port);
+            if (log.isDebugEnabled()) {
+                log.debug("startDynamicPortForwarding(" + local + "): " + result);
+            }
+
+            signalEstablishedDynamicTunnel(local, result, null);
+            return result;
+        } catch (IOException | RuntimeException e) {
+            stopDynamicPortForwarding(local);
+            throw e;
+        }
+    }
+
+    protected void signalEstablishedDynamicTunnel(
+            SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
+                    throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalEstablishedDynamicTunnel(l, local, boundAddress, reason);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal establishing dynamic tunnel for local=" + local
+                        + " on bound=" + boundAddress, t);
+            }
+        }
+    }
+
+    protected void signalEstablishedDynamicTunnel(PortForwardingEventListener listener,
+                SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.establishedDynamicTunnel(getSession(), local, boundAddress, reason);
+    }
+
+    protected void signalEstablishingDynamicTunnel(SshdSocketAddress local) throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalEstablishingDynamicTunnel(l, local);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal establishing dynamic tunnel for local=" + local, t);
+            }
+        }
+    }
+
+    protected void signalEstablishingDynamicTunnel(PortForwardingEventListener listener, SshdSocketAddress local) throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.establishingDynamicTunnel(getSession(), local);
+    }
+
+    @Override
+    public synchronized void stopDynamicPortForwarding(SshdSocketAddress local) throws IOException {
+        SocksProxy obj;
+        synchronized (dynamicLocal) {
+            obj = dynamicLocal.remove(local.getPort());
+        }
+
+        if (obj != null) {
+            if (log.isDebugEnabled()) {
+                log.debug("stopDynamicPortForwarding(" + local + ") unbinding");
+            }
+
+            signalTearingDownDynamicTunnel(local);
+            try {
+                obj.close(true);
+                acceptor.unbind(local.toInetSocketAddress());
+            } catch (RuntimeException e) {
+                signalTornDownDynamicTunnel(local, e);
+                throw e;
+            }
+
+            signalTornDownDynamicTunnel(local, null);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("stopDynamicPortForwarding(" + local + ") no binding found");
+            }
+        }
+    }
+
+    protected void signalTearingDownDynamicTunnel(SshdSocketAddress address) throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalTearingDownDynamicTunnel(l, address);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal tearing down dynamic tunnel for address=" + address, t);
+            }
+        }
+    }
+
+    protected void signalTearingDownDynamicTunnel(PortForwardingEventListener listener, SshdSocketAddress address) throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.tearingDownDynamicTunnel(getSession(), address);
+    }
+
+    protected void signalTornDownDynamicTunnel(SshdSocketAddress address, Throwable reason) throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalTornDownDynamicTunnel(l, address, reason);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal torn down dynamic tunnel for address=" + address, t);
+            }
+        }
+    }
+
+    protected void signalTornDownDynamicTunnel(
+            PortForwardingEventListener listener, SshdSocketAddress address, Throwable reason)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.tornDownDynamicTunnel(getSession(), address, reason);
+    }
+
+    @Override
+    public synchronized SshdSocketAddress getForwardedPort(int remotePort) {
+        synchronized (remoteToLocal) {
+            return remoteToLocal.get(remotePort);
+        }
+    }
+
+    @Override
+    public synchronized SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException {
+        Objects.requireNonNull(local, "Local address is null");
+        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
+
+        Session session = getSession();
+        FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
+        TcpForwardingFilter filter = manager.getTcpForwardingFilter();
+        try {
+            if ((filter == null) || (!filter.canListen(local, session))) {
+                if (log.isDebugEnabled()) {
+                    log.debug("localPortForwardingRequested(" + session + ")[" + local + "][haveFilter=" + (filter != null) + "] rejected");
+                }
+                return null;
+            }
+        } catch (Error e) {
+            log.warn("localPortForwardingRequested({})[{}] failed ({}) to consult forwarding filter: {}",
+                     session, local, e.getClass().getSimpleName(), e.getMessage());
+            if (log.isDebugEnabled()) {
+                log.debug("localPortForwardingRequested(" + this + ")[" + local + "] filter consultation failure details", e);
+            }
+            throw new RuntimeSshException(e);
+        }
+
+        signalEstablishingExplicitTunnel(local, null, true);
+        SshdSocketAddress result;
+        try {
+            InetSocketAddress bound = doBind(local, staticIoHandlerFactory);
+            result = new SshdSocketAddress(bound.getHostString(), bound.getPort());
+            if (log.isDebugEnabled()) {
+                log.debug("localPortForwardingRequested(" + local + "): " + result);
+            }
+
+            boolean added;
+            synchronized (localForwards) {
+                // NOTE !!! it is crucial to use the bound address host name first
+                added = localForwards.add(new LocalForwardingEntry(result.getHostName(), local.getHostName(), result.getPort()));
+            }
+
+            if (!added) {
+                throw new IOException("Failed to add local port forwarding entry for " + local + " -> " + result);
+            }
+        } catch (IOException | RuntimeException e) {
+            try {
+                localPortForwardingCancelled(local);
+            } catch (IOException | RuntimeException err) {
+                e.addSuppressed(e);
+            }
+            signalEstablishedExplicitTunnel(local, null, true, null, e);
+            throw e;
+        }
+
+        try {
+            signalEstablishedExplicitTunnel(local, null, true, result, null);
+            return result;
+        } catch (IOException | RuntimeException e) {
+            throw e;
+        }
+    }
+
+    @Override
+    public synchronized void localPortForwardingCancelled(SshdSocketAddress local) throws IOException {
+        LocalForwardingEntry entry;
+        synchronized (localForwards) {
+            entry = LocalForwardingEntry.findMatchingEntry(local.getHostName(), local.getPort(), localForwards);
+            if (entry != null) {
+                localForwards.remove(entry);
+            }
+        }
+
+        if ((entry != null) && (acceptor != null)) {
+            if (log.isDebugEnabled()) {
+                log.debug("localPortForwardingCancelled(" + local + ") unbind " + entry);
+            }
+
+            signalTearingDownExplicitTunnel(entry, true);
+            try {
+                acceptor.unbind(entry.toInetSocketAddress());
+            } catch (RuntimeException e) {
+                signalTornDownExplicitTunnel(entry, true, e);
+                throw e;
+            }
+
+            signalTornDownExplicitTunnel(entry, true, null);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("localPortForwardingCancelled(" + local + ") no match/acceptor: " + entry);
+            }
+        }
+    }
+
+    protected void signalEstablishingExplicitTunnel(
+            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding)
+                    throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalEstablishingExplicitTunnel(l, local, remote, localForwarding);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal establishing explicit tunnel for local=" + local
+                        + ", remote=" + remote + ", localForwarding=" + localForwarding, t);
+            }
+        }
+    }
+
+    protected void signalEstablishingExplicitTunnel(PortForwardingEventListener listener,
+            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.establishingExplicitTunnel(getSession(), local, remote, localForwarding);
+    }
+
+    protected void signalEstablishedExplicitTunnel(
+            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding,
+            SshdSocketAddress boundAddress, Throwable reason)
+                    throws IOException {
+        try {
+            invokePortEventListenerSignaller(l -> {
+                signalEstablishedExplicitTunnel(l, local, remote, localForwarding, boundAddress, reason);
+                return null;
+            });
+        } catch (Throwable t) {
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            } else if (t instanceof Error) {
+                throw (Error) t;
+            } else if (t instanceof IOException) {
+                throw (IOException) t;
+            } else {
+                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
+                        + " to signal established explicit tunnel for local=" + local
+                        + ", remote=" + remote + ", localForwarding=" + localForwarding
+                        + ", bound=" + boundAddress, t);
+            }
+        }
+    }
+
+    protected void signalEstablishedExplicitTunnel(PortForwardingEventListener listener,
+            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding,
+            SshdSocketAddress boundAddress, Throwable reason)
+                    throws IOException {
+        if (listener == null) {
+            return;
+        }
+
+        listener.establishedExplicitTunnel(getSession(), local, remote, localForwarding, boundAddress, reason);
+    }
+
+    protected void invokePortEventListenerSignaller(Invoker<PortForwardingEventListener, Void> invoker) throws Throwable {
+        Throwable err = null;
+        try {
+            invokePortEventListenerSignallerListeners(getDefaultListeners(), invoker);
+        } catch (Throwable t) {
+            Throwable e = GenericUtils.peelException(t);
+            err = GenericUtils.accumulateException(err, e);
+        }
+
+        try {
+            invokePortEventListenerSignallerHolders(managersHolder, invoker);
+        } catch (Throwable t) {
+            Throwable e = GenericUtils.peelException(t);
+            err = GenericUtils.accumulateException(err, e);
+        }
+
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    protected void invokePortEventListenerSignallerListeners(
+            Collection<? extends PortForwardingEventListener> listeners, Invoker<PortForwardingEventListener, Void> invoker)
+                    throws Throwable {
+        if (GenericUtils.isEmpty(listeners)) {
+            return;
+        }
+
+        Throwable err = null;
+        // Need to go over the hierarchy (session, factory managed, connection service, etc...)
+        for (PortForwardingEventListener l : listeners) {
+            if (l == null) {
+                continue;
+            }
+
+            try {
+                invoker.invoke(l);
+            } catch (Throwable t) {
+                Throwable e = GenericUtils.peelException(t);
+                err = GenericUtils.accumulateException(err, e);
+            }
+        }
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    protected void invokePortEventListenerSignallerHolders(
+            Collection<? extends PortForwardingEventListenerManager> holders, Invoker<PortForwardingEventListener, Void> invoker)
+                    throws Throwable {
+        if (GenericUtils.isEmpty(holders)) {
+            return;
+        }
+
+        Throwable err = null;
+        // Need to go over the hierarchy (session, factory managed, connection service, etc...)
+        for (PortForwardingEventListenerManager m : holders) {
+            try {
+                PortForwardingEventListener listener = m.getPortForwardingEventListenerProxy();
+                if (listener != null) {
+                    invoker.invoke(listener);
+                }
+            } catch (Throwable t) {
+                Throwable e = GenericUtils.peelException(t);
+                err = GenericUtils.accumulateException(err, e);
+            }
+
+            if (m instanceof PortForwardingEventListenerManagerHolder) {
+                try {
+                    invokePortEventListenerSignallerHolders(((PortForwardingEventListenerManagerHolder) m).getRegisteredManagers(), invoker);
+                } catch (Throwable t) {
+                    Throwable e = GenericUtils.peelException(t);
+                    err = GenericUtils.accumulateException(err, e);
+                }
+            }
+        }
+
+        if (err != null) {
+            throw err;
+        }
+    }
+
+    @Override
+    protected synchronized Closeable getInnerCloseable() {
+        return builder().parallel(dynamicLocal.values()).close(acceptor).build();
+    }
+
+    @Override
+    protected void preClose() {
+        this.listeners.clear();
+        this.managersHolder.clear();
+        super.preClose();
+    }
+
+    /**
+     * @param address        The request bind address
+     * @param handlerFactory A {@link Factory} to create an {@link IoHandler} if necessary
+     * @return The {@link InetSocketAddress} to which the binding occurred
+     * @throws IOException If failed to bind
+     */
+    private InetSocketAddress doBind(SshdSocketAddress address, Factory<? extends IoHandler> handlerFactory) throws IOException {
+        if (acceptor == null) {
+            Session session = getSession();
+            FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
+            IoServiceFactory factory = Objects.requireNonNull(manager.getIoServiceFactory(), "No I/O service factory");
+            IoHandler handler = handlerFactory.create();
+            acceptor = factory.createAcceptor(handler);
+        }
+
+        // TODO find a better way to determine the resulting bind address - what if multi-threaded calls...
+        Set<SocketAddress> before = acceptor.getBoundAddresses();
+        try {
+            InetSocketAddress bindAddress = address.toInetSocketAddress();
+            acceptor.bind(bindAddress);
+
+            Set<SocketAddress> after = acceptor.getBoundAddresses();
+            if (GenericUtils.size(after) > 0) {
+                after.removeAll(before);
+            }
+            if (GenericUtils.isEmpty(after)) {
+                throw new IOException("Error binding to " + address + "[" + bindAddress + "]: no local addresses bound");
+            }
+
+            if (after.size() > 1) {
+                throw new IOException("Multiple local addresses have been bound for " + address + "[" + bindAddress + "]");
+            }
+            return (InetSocketAddress) after.iterator().next();
+        } catch (IOException bindErr) {
+            Set<SocketAddress> after = acceptor.getBoundAddresses();
+            if (GenericUtils.isEmpty(after)) {
+                close();
+            }
+            throw bindErr;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + getSession() + "]";
+    }
+
+    //
+    // Static IoHandler implementation
+    //
+
+    class StaticIoHandler implements IoHandler {
+        StaticIoHandler() {
+            super();
+        }
+
+        @Override
+        @SuppressWarnings("synthetic-access")
+        public void sessionCreated(final IoSession session) throws Exception {
+            InetSocketAddress local = (InetSocketAddress) session.getLocalAddress();
+            int localPort = local.getPort();
+            SshdSocketAddress remote = localToRemote.get(localPort);
+            if (log.isDebugEnabled()) {
+                log.debug("sessionCreated({}) remote={}", session, remote);
+            }
+
+            final TcpipClientChannel channel;
+            if (remote != null) {
+                channel = new TcpipClientChannel(TcpipClientChannel.Type.Direct, session, remote);
+            } else {
+                channel = new TcpipClientChannel(TcpipClientChannel.Type.Forwarded, session, null);
+            }
+            session.setAttribute(TcpipClientChannel.class, channel);
+
+            service.registerChannel(channel);
+            channel.open().addListener(future -> {
+                Throwable t = future.getException();
+                if (t != null) {
+                    log.warn("Failed ({}) to open channel for session={}: {}",
+                             t.getClass().getSimpleName(), session, t.getMessage());
+                    if (log.isDebugEnabled()) {
+                        log.debug("sessionCreated(" + session + ") channel=" + channel + " open failure details", t);
+                    }
+                    DefaultForwardingFilter.this.service.unregisterChannel(channel);
+                    channel.close(false);
+                }
+            });
+        }
+
+        @Override
+        @SuppressWarnings("synthetic-access")
+        public void sessionClosed(IoSession session) throws Exception {
+            TcpipClientChannel channel = (TcpipClientChannel) session.getAttribute(TcpipClientChannel.class);
+            if (channel != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("sessionClosed({}) closing channel={}", session, channel);
+                }
+                channel.close(false);
+            }
+        }
+
+        @Override
+        @SuppressWarnings("synthetic-access")
+        public void messageReceived(IoSession session, Readable message) throws Exception {
+            TcpipClientChannel channel = (TcpipClientChannel) session.getAttribute(TcpipClientChannel.class);
+            Buffer buffer = new ByteArrayBuffer(message.available() + Long.SIZE, false);
+            buffer.putBuffer(message);
+
+            Collection<ClientChannelEvent> result = channel.waitFor(STATIC_IO_MSG_RECEIVED_EVENTS, Long.MAX_VALUE);
+            if (log.isTraceEnabled()) {
+                log.trace("messageReceived({}) channel={}, len={} wait result: {}",
+                          session, channel, result, buffer.array());
+            }
+
+            OutputStream outputStream = channel.getInvertedIn();
+            outputStream.write(buffer.array(), buffer.rpos(), buffer.available());
+            outputStream.flush();
+        }
+
+        @Override
+        @SuppressWarnings("synthetic-access")
+        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
+            if (log.isDebugEnabled()) {
+                log.debug("exceptionCaught({}) {}: {}", session, cause.getClass().getSimpleName(), cause.getMessage());
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("exceptionCaught(" + session + ") caught exception details", cause);
+            }
+            session.close(false);
+        }
+    }
+}


[2/3] mina-sshd git commit: [SSHD-766] Separate forwarding filter functionality according to sshd-config options

Posted by lg...@apache.org.
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
deleted file mode 100644
index 23fe01c..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
+++ /dev/null
@@ -1,1007 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.forward;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.sshd.client.channel.ClientChannelEvent;
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.Factory;
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.io.IoAcceptor;
-import org.apache.sshd.common.io.IoHandler;
-import org.apache.sshd.common.io.IoHandlerFactory;
-import org.apache.sshd.common.io.IoServiceFactory;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionHolder;
-import org.apache.sshd.common.util.EventListenerUtils;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.Invoker;
-import org.apache.sshd.common.util.Readable;
-import org.apache.sshd.common.util.ValidateUtils;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.closeable.AbstractInnerCloseable;
-import org.apache.sshd.common.util.net.SshdSocketAddress;
-import org.apache.sshd.server.forward.ForwardingFilter;
-
-/**
- * Requests a &quot;tcpip-forward&quot; action
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultTcpipForwarder
-        extends AbstractInnerCloseable
-        implements TcpipForwarder, SessionHolder<Session>, PortForwardingEventListenerManager {
-
-    /**
-     * Used to configure the timeout (milliseconds) for receiving a response
-     * for the forwarding request
-     *
-     * @see #DEFAULT_FORWARD_REQUEST_TIMEOUT
-     */
-    public static final String FORWARD_REQUEST_TIMEOUT = "tcpip-forward-request-timeout";
-
-    /**
-     * Default value for {@link #FORWARD_REQUEST_TIMEOUT} if none specified
-     */
-    public static final long DEFAULT_FORWARD_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(15L);
-
-    public static final Set<ClientChannelEvent> STATIC_IO_MSG_RECEIVED_EVENTS =
-            Collections.unmodifiableSet(EnumSet.of(ClientChannelEvent.OPENED, ClientChannelEvent.CLOSED));
-
-    private final ConnectionService service;
-    private final IoHandlerFactory socksProxyIoHandlerFactory = () -> new SocksProxy(getConnectionService());
-    private final Session sessionInstance;
-    private final Map<Integer, SshdSocketAddress> localToRemote = new TreeMap<>(Comparator.naturalOrder());
-    private final Map<Integer, SshdSocketAddress> remoteToLocal = new TreeMap<>(Comparator.naturalOrder());
-    private final Map<Integer, SocksProxy> dynamicLocal = new TreeMap<>(Comparator.naturalOrder());
-    private final Set<LocalForwardingEntry> localForwards = new HashSet<>();
-    private final IoHandlerFactory staticIoHandlerFactory = StaticIoHandler::new;
-    private final Collection<PortForwardingEventListener> listeners = new CopyOnWriteArraySet<>();
-    private final Collection<PortForwardingEventListenerManager> managersHolder = new CopyOnWriteArraySet<>();
-    private final PortForwardingEventListener listenerProxy;
-
-    private IoAcceptor acceptor;
-
-    public DefaultTcpipForwarder(ConnectionService service) {
-        this.service = Objects.requireNonNull(service, "No connection service");
-        this.sessionInstance = Objects.requireNonNull(service.getSession(), "No session");
-        this.listenerProxy = EventListenerUtils.proxyWrapper(PortForwardingEventListener.class, getClass().getClassLoader(), listeners);
-    }
-
-    @Override
-    public PortForwardingEventListener getPortForwardingEventListenerProxy() {
-        return listenerProxy;
-    }
-
-    @Override
-    public void addPortForwardingEventListener(PortForwardingEventListener listener) {
-        listeners.add(PortForwardingEventListener.validateListener(listener));
-    }
-
-    @Override
-    public void removePortForwardingEventListener(PortForwardingEventListener listener) {
-        if (listener == null) {
-            return;
-        }
-
-        listeners.remove(PortForwardingEventListener.validateListener(listener));
-    }
-
-    @Override
-    public Collection<PortForwardingEventListenerManager> getRegisteredManagers() {
-        return managersHolder.isEmpty() ? Collections.emptyList() : new ArrayList<>(managersHolder);
-    }
-
-    @Override
-    public boolean addPortForwardingEventListenerManager(PortForwardingEventListenerManager manager) {
-        return managersHolder.add(Objects.requireNonNull(manager, "No manager"));
-    }
-
-    @Override
-    public boolean removePortForwardingEventListenerManager(PortForwardingEventListenerManager manager) {
-        if (manager == null) {
-            return false;
-        }
-
-        return managersHolder.remove(manager);
-    }
-
-    @Override
-    public Session getSession() {
-        return sessionInstance;
-    }
-
-    public final ConnectionService getConnectionService() {
-        return service;
-    }
-
-    protected Collection<PortForwardingEventListener> getDefaultListeners() {
-        Collection<PortForwardingEventListener> defaultListeners = new ArrayList<>();
-        defaultListeners.add(getPortForwardingEventListenerProxy());
-
-        Session session = getSession();
-        PortForwardingEventListener l = session.getPortForwardingEventListenerProxy();
-        if (l != null) {
-            defaultListeners.add(l);
-        }
-
-        FactoryManager manager = (session == null) ? null : session.getFactoryManager();
-        l = (manager == null) ? null : manager.getPortForwardingEventListenerProxy();
-        if (l != null) {
-            defaultListeners.add(l);
-        }
-
-        return defaultListeners;
-    }
-
-    //
-    // TcpIpForwarder implementation
-    //
-
-    @Override
-    public synchronized SshdSocketAddress startLocalPortForwarding(SshdSocketAddress local, SshdSocketAddress remote) throws IOException {
-        Objects.requireNonNull(local, "Local address is null");
-        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
-        Objects.requireNonNull(remote, "Remote address is null");
-
-        if (isClosed()) {
-            throw new IllegalStateException("TcpipForwarder is closed");
-        }
-        if (isClosing()) {
-            throw new IllegalStateException("TcpipForwarder is closing");
-        }
-
-        InetSocketAddress bound;
-        int port;
-        signalEstablishingExplicitTunnel(local, remote, true);
-        try {
-            bound = doBind(local, staticIoHandlerFactory);
-            port = bound.getPort();
-            SshdSocketAddress prev;
-            synchronized (localToRemote) {
-                prev = localToRemote.put(port, remote);
-            }
-
-            if (prev != null) {
-                throw new IOException("Multiple local port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev);
-            }
-        } catch (IOException | RuntimeException e) {
-            try {
-                stopLocalPortForwarding(local);
-            } catch (IOException | RuntimeException err) {
-                e.addSuppressed(err);
-            }
-            signalEstablishedExplicitTunnel(local, remote, true, null, e);
-            throw e;
-        }
-
-        try {
-            SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port);
-            if (log.isDebugEnabled()) {
-                log.debug("startLocalPortForwarding(" + local + " -> " + remote + "): " + result);
-            }
-            signalEstablishedExplicitTunnel(local, remote, true, result, null);
-            return result;
-        } catch (IOException | RuntimeException e) {
-            stopLocalPortForwarding(local);
-            throw e;
-        }
-    }
-
-    @Override
-    public synchronized void stopLocalPortForwarding(SshdSocketAddress local) throws IOException {
-        Objects.requireNonNull(local, "Local address is null");
-
-        SshdSocketAddress bound;
-        synchronized (localToRemote) {
-            bound = localToRemote.remove(local.getPort());
-        }
-
-        if ((bound != null) && (acceptor != null)) {
-            if (log.isDebugEnabled()) {
-                log.debug("stopLocalPortForwarding(" + local + ") unbind " + bound);
-            }
-
-            signalTearingDownExplicitTunnel(bound, true);
-            try {
-                acceptor.unbind(bound.toInetSocketAddress());
-            } catch (RuntimeException e) {
-                signalTornDownExplicitTunnel(bound, true, e);
-                throw e;
-            }
-
-            signalTornDownExplicitTunnel(bound, true, null);
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("stopLocalPortForwarding(" + local + ") no mapping/acceptor for " + bound);
-            }
-        }
-    }
-
-    @Override
-    public synchronized SshdSocketAddress startRemotePortForwarding(SshdSocketAddress remote, SshdSocketAddress local) throws IOException {
-        Objects.requireNonNull(local, "Local address is null");
-        Objects.requireNonNull(remote, "Remote address is null");
-
-        String remoteHost = remote.getHostName();
-        int remotePort = remote.getPort();
-        Session session = getSession();
-        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, remoteHost.length() + Long.SIZE);
-        buffer.putString("tcpip-forward");
-        buffer.putBoolean(true);    // want reply
-        buffer.putString(remoteHost);
-        buffer.putInt(remotePort);
-
-        long timeout = session.getLongProperty(FORWARD_REQUEST_TIMEOUT, DEFAULT_FORWARD_REQUEST_TIMEOUT);
-        Buffer result;
-        int port;
-        signalEstablishingExplicitTunnel(local, remote, false);
-        try {
-            result = session.request("tcpip-forward", buffer, timeout, TimeUnit.MILLISECONDS);
-            if (result == null) {
-                throw new SshException("Tcpip forwarding request denied by server");
-            }
-            port = (remotePort == 0) ? result.getInt() : remote.getPort();
-            // TODO: Is it really safe to only store the local address after the request ?
-            SshdSocketAddress prev;
-            synchronized (remoteToLocal) {
-                prev = remoteToLocal.put(port, local);
-            }
-
-            if (prev != null) {
-                throw new IOException("Multiple remote port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev);
-            }
-        } catch (IOException | RuntimeException e) {
-            try {
-                stopRemotePortForwarding(remote);
-            } catch (IOException | RuntimeException err) {
-                e.addSuppressed(err);
-            }
-            signalEstablishedExplicitTunnel(local, remote, false, null, e);
-            throw e;
-        }
-
-        try {
-            SshdSocketAddress bound = new SshdSocketAddress(remoteHost, port);
-            if (log.isDebugEnabled()) {
-                log.debug("startRemotePortForwarding(" + remote + " -> " + local + "): " + bound);
-            }
-
-            signalEstablishedExplicitTunnel(local, remote, false, bound, null);
-            return bound;
-        } catch (IOException | RuntimeException e) {
-            stopRemotePortForwarding(remote);
-            throw e;
-        }
-    }
-
-    @Override
-    public synchronized void stopRemotePortForwarding(SshdSocketAddress remote) throws IOException {
-        SshdSocketAddress bound;
-        synchronized (remoteToLocal) {
-            bound = remoteToLocal.remove(remote.getPort());
-        }
-
-        if (bound != null) {
-            if (log.isDebugEnabled()) {
-                log.debug("stopRemotePortForwarding(" + remote + ") cancel forwarding to " + bound);
-            }
-
-            String remoteHost = remote.getHostName();
-            Session session = getSession();
-            Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, remoteHost.length() + Long.SIZE);
-            buffer.putString("cancel-tcpip-forward");
-            buffer.putBoolean(false);   // want reply
-            buffer.putString(remoteHost);
-            buffer.putInt(remote.getPort());
-
-            signalTearingDownExplicitTunnel(bound, false);
-            try {
-                session.writePacket(buffer);
-            } catch (IOException | RuntimeException e) {
-                signalTornDownExplicitTunnel(bound, false, e);
-                throw e;
-            }
-
-            signalTornDownExplicitTunnel(bound, false, null);
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("stopRemotePortForwarding(" + remote + ") no binding found");
-            }
-        }
-    }
-
-    protected void signalTearingDownExplicitTunnel(SshdSocketAddress boundAddress, boolean localForwarding) throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalTearingDownExplicitTunnel(l, boundAddress, localForwarding);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal tearing down explicit tunnel for local=" + localForwarding
-                        + " on bound=" + boundAddress, t);
-            }
-        }
-    }
-
-    protected void signalTearingDownExplicitTunnel(
-            PortForwardingEventListener listener, SshdSocketAddress boundAddress, boolean localForwarding)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.tearingDownExplicitTunnel(getSession(), boundAddress, localForwarding);
-    }
-
-    protected void signalTornDownExplicitTunnel(SshdSocketAddress boundAddress, boolean localForwarding, Throwable reason) throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalTornDownExplicitTunnel(l, boundAddress, localForwarding, reason);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal torn down explicit tunnel local=" + localForwarding
-                        + " on bound=" + boundAddress, t);
-            }
-        }
-    }
-
-    protected void signalTornDownExplicitTunnel(
-            PortForwardingEventListener listener, SshdSocketAddress boundAddress, boolean localForwarding, Throwable reason)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.tornDownExplicitTunnel(getSession(), boundAddress, localForwarding, reason);
-    }
-
-    @Override
-    public synchronized SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        Objects.requireNonNull(local, "Local address is null");
-        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
-
-        if (isClosed()) {
-            throw new IllegalStateException("TcpipForwarder is closed");
-        }
-        if (isClosing()) {
-            throw new IllegalStateException("TcpipForwarder is closing");
-        }
-
-        SocksProxy socksProxy = new SocksProxy(service);
-        SocksProxy prev;
-        InetSocketAddress bound;
-        int port;
-        signalEstablishingDynamicTunnel(local);
-        try {
-            bound = doBind(local, socksProxyIoHandlerFactory);
-            port = bound.getPort();
-            synchronized (dynamicLocal) {
-                prev = dynamicLocal.put(port, socksProxy);
-            }
-
-            if (prev != null) {
-                throw new IOException("Multiple dynamic port mappings found for port=" + port + ": current=" + socksProxy + ", previous=" + prev);
-            }
-        } catch (IOException | RuntimeException e) {
-            try {
-                stopDynamicPortForwarding(local);
-            } catch (IOException | RuntimeException err) {
-                e.addSuppressed(err);
-            }
-            signalEstablishedDynamicTunnel(local, null, e);
-            throw e;
-        }
-
-        try {
-            SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port);
-            if (log.isDebugEnabled()) {
-                log.debug("startDynamicPortForwarding(" + local + "): " + result);
-            }
-
-            signalEstablishedDynamicTunnel(local, result, null);
-            return result;
-        } catch (IOException | RuntimeException e) {
-            stopDynamicPortForwarding(local);
-            throw e;
-        }
-    }
-
-    protected void signalEstablishedDynamicTunnel(
-            SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
-                    throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalEstablishedDynamicTunnel(l, local, boundAddress, reason);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal establishing dynamic tunnel for local=" + local
-                        + " on bound=" + boundAddress, t);
-            }
-        }
-    }
-
-    protected void signalEstablishedDynamicTunnel(PortForwardingEventListener listener,
-                SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.establishedDynamicTunnel(getSession(), local, boundAddress, reason);
-    }
-
-    protected void signalEstablishingDynamicTunnel(SshdSocketAddress local) throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalEstablishingDynamicTunnel(l, local);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal establishing dynamic tunnel for local=" + local, t);
-            }
-        }
-    }
-
-    protected void signalEstablishingDynamicTunnel(PortForwardingEventListener listener, SshdSocketAddress local) throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.establishingDynamicTunnel(getSession(), local);
-    }
-
-    @Override
-    public synchronized void stopDynamicPortForwarding(SshdSocketAddress local) throws IOException {
-        SocksProxy obj;
-        synchronized (dynamicLocal) {
-            obj = dynamicLocal.remove(local.getPort());
-        }
-
-        if (obj != null) {
-            if (log.isDebugEnabled()) {
-                log.debug("stopDynamicPortForwarding(" + local + ") unbinding");
-            }
-
-            signalTearingDownDynamicTunnel(local);
-            try {
-                obj.close(true);
-                acceptor.unbind(local.toInetSocketAddress());
-            } catch (RuntimeException e) {
-                signalTornDownDynamicTunnel(local, e);
-                throw e;
-            }
-
-            signalTornDownDynamicTunnel(local, null);
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("stopDynamicPortForwarding(" + local + ") no binding found");
-            }
-        }
-    }
-
-    protected void signalTearingDownDynamicTunnel(SshdSocketAddress address) throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalTearingDownDynamicTunnel(l, address);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal tearing down dynamic tunnel for address=" + address, t);
-            }
-        }
-    }
-
-    protected void signalTearingDownDynamicTunnel(PortForwardingEventListener listener, SshdSocketAddress address) throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.tearingDownDynamicTunnel(getSession(), address);
-    }
-
-    protected void signalTornDownDynamicTunnel(SshdSocketAddress address, Throwable reason) throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalTornDownDynamicTunnel(l, address, reason);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal torn down dynamic tunnel for address=" + address, t);
-            }
-        }
-    }
-
-    protected void signalTornDownDynamicTunnel(
-            PortForwardingEventListener listener, SshdSocketAddress address, Throwable reason)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.tornDownDynamicTunnel(getSession(), address, reason);
-    }
-
-    @Override
-    public synchronized SshdSocketAddress getForwardedPort(int remotePort) {
-        synchronized (remoteToLocal) {
-            return remoteToLocal.get(remotePort);
-        }
-    }
-
-    @Override
-    public synchronized SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException {
-        Objects.requireNonNull(local, "Local address is null");
-        ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local);
-
-        Session session = getSession();
-        FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        ForwardingFilter filter = manager.getTcpipForwardingFilter();
-        try {
-            if ((filter == null) || (!filter.canListen(local, session))) {
-                if (log.isDebugEnabled()) {
-                    log.debug("localPortForwardingRequested(" + session + ")[" + local + "][haveFilter=" + (filter != null) + "] rejected");
-                }
-                return null;
-            }
-        } catch (Error e) {
-            log.warn("localPortForwardingRequested({})[{}] failed ({}) to consult forwarding filter: {}",
-                     session, local, e.getClass().getSimpleName(), e.getMessage());
-            if (log.isDebugEnabled()) {
-                log.debug("localPortForwardingRequested(" + this + ")[" + local + "] filter consultation failure details", e);
-            }
-            throw new RuntimeSshException(e);
-        }
-
-        signalEstablishingExplicitTunnel(local, null, true);
-        SshdSocketAddress result;
-        try {
-            InetSocketAddress bound = doBind(local, staticIoHandlerFactory);
-            result = new SshdSocketAddress(bound.getHostString(), bound.getPort());
-            if (log.isDebugEnabled()) {
-                log.debug("localPortForwardingRequested(" + local + "): " + result);
-            }
-
-            boolean added;
-            synchronized (localForwards) {
-                // NOTE !!! it is crucial to use the bound address host name first
-                added = localForwards.add(new LocalForwardingEntry(result.getHostName(), local.getHostName(), result.getPort()));
-            }
-
-            if (!added) {
-                throw new IOException("Failed to add local port forwarding entry for " + local + " -> " + result);
-            }
-        } catch (IOException | RuntimeException e) {
-            try {
-                localPortForwardingCancelled(local);
-            } catch (IOException | RuntimeException err) {
-                e.addSuppressed(e);
-            }
-            signalEstablishedExplicitTunnel(local, null, true, null, e);
-            throw e;
-        }
-
-        try {
-            signalEstablishedExplicitTunnel(local, null, true, result, null);
-            return result;
-        } catch (IOException | RuntimeException e) {
-            throw e;
-        }
-    }
-
-    @Override
-    public synchronized void localPortForwardingCancelled(SshdSocketAddress local) throws IOException {
-        LocalForwardingEntry entry;
-        synchronized (localForwards) {
-            entry = LocalForwardingEntry.findMatchingEntry(local.getHostName(), local.getPort(), localForwards);
-            if (entry != null) {
-                localForwards.remove(entry);
-            }
-        }
-
-        if ((entry != null) && (acceptor != null)) {
-            if (log.isDebugEnabled()) {
-                log.debug("localPortForwardingCancelled(" + local + ") unbind " + entry);
-            }
-
-            signalTearingDownExplicitTunnel(entry, true);
-            try {
-                acceptor.unbind(entry.toInetSocketAddress());
-            } catch (RuntimeException e) {
-                signalTornDownExplicitTunnel(entry, true, e);
-                throw e;
-            }
-
-            signalTornDownExplicitTunnel(entry, true, null);
-        } else {
-            if (log.isDebugEnabled()) {
-                log.debug("localPortForwardingCancelled(" + local + ") no match/acceptor: " + entry);
-            }
-        }
-    }
-
-    protected void signalEstablishingExplicitTunnel(
-            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding)
-                    throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalEstablishingExplicitTunnel(l, local, remote, localForwarding);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal establishing explicit tunnel for local=" + local
-                        + ", remote=" + remote + ", localForwarding=" + localForwarding, t);
-            }
-        }
-    }
-
-    protected void signalEstablishingExplicitTunnel(PortForwardingEventListener listener,
-            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.establishingExplicitTunnel(getSession(), local, remote, localForwarding);
-    }
-
-    protected void signalEstablishedExplicitTunnel(
-            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding,
-            SshdSocketAddress boundAddress, Throwable reason)
-                    throws IOException {
-        try {
-            invokePortEventListenerSignaller(l -> {
-                signalEstablishedExplicitTunnel(l, local, remote, localForwarding, boundAddress, reason);
-                return null;
-            });
-        } catch (Throwable t) {
-            if (t instanceof RuntimeException) {
-                throw (RuntimeException) t;
-            } else if (t instanceof Error) {
-                throw (Error) t;
-            } else if (t instanceof IOException) {
-                throw (IOException) t;
-            } else {
-                throw new IOException("Failed (" + t.getClass().getSimpleName() + ")"
-                        + " to signal established explicit tunnel for local=" + local
-                        + ", remote=" + remote + ", localForwarding=" + localForwarding
-                        + ", bound=" + boundAddress, t);
-            }
-        }
-    }
-
-    protected void signalEstablishedExplicitTunnel(PortForwardingEventListener listener,
-            SshdSocketAddress local, SshdSocketAddress remote, boolean localForwarding,
-            SshdSocketAddress boundAddress, Throwable reason)
-                    throws IOException {
-        if (listener == null) {
-            return;
-        }
-
-        listener.establishedExplicitTunnel(getSession(), local, remote, localForwarding, boundAddress, reason);
-    }
-
-    protected void invokePortEventListenerSignaller(Invoker<PortForwardingEventListener, Void> invoker) throws Throwable {
-        Throwable err = null;
-        try {
-            invokePortEventListenerSignallerListeners(getDefaultListeners(), invoker);
-        } catch (Throwable t) {
-            Throwable e = GenericUtils.peelException(t);
-            err = GenericUtils.accumulateException(err, e);
-        }
-
-        try {
-            invokePortEventListenerSignallerHolders(managersHolder, invoker);
-        } catch (Throwable t) {
-            Throwable e = GenericUtils.peelException(t);
-            err = GenericUtils.accumulateException(err, e);
-        }
-
-
-        if (err != null) {
-            throw err;
-        }
-    }
-
-    protected void invokePortEventListenerSignallerListeners(
-            Collection<? extends PortForwardingEventListener> listeners, Invoker<PortForwardingEventListener, Void> invoker)
-                    throws Throwable {
-        if (GenericUtils.isEmpty(listeners)) {
-            return;
-        }
-
-        Throwable err = null;
-        // Need to go over the hierarchy (session, factory managed, connection service, etc...)
-        for (PortForwardingEventListener l : listeners) {
-            if (l == null) {
-                continue;
-            }
-
-            try {
-                invoker.invoke(l);
-            } catch (Throwable t) {
-                Throwable e = GenericUtils.peelException(t);
-                err = GenericUtils.accumulateException(err, e);
-            }
-        }
-
-        if (err != null) {
-            throw err;
-        }
-    }
-
-    protected void invokePortEventListenerSignallerHolders(
-            Collection<? extends PortForwardingEventListenerManager> holders, Invoker<PortForwardingEventListener, Void> invoker)
-                    throws Throwable {
-        if (GenericUtils.isEmpty(holders)) {
-            return;
-        }
-
-        Throwable err = null;
-        // Need to go over the hierarchy (session, factory managed, connection service, etc...)
-        for (PortForwardingEventListenerManager m : holders) {
-            try {
-                PortForwardingEventListener listener = m.getPortForwardingEventListenerProxy();
-                if (listener != null) {
-                    invoker.invoke(listener);
-                }
-            } catch (Throwable t) {
-                Throwable e = GenericUtils.peelException(t);
-                err = GenericUtils.accumulateException(err, e);
-            }
-
-            if (m instanceof PortForwardingEventListenerManagerHolder) {
-                try {
-                    invokePortEventListenerSignallerHolders(((PortForwardingEventListenerManagerHolder) m).getRegisteredManagers(), invoker);
-                } catch (Throwable t) {
-                    Throwable e = GenericUtils.peelException(t);
-                    err = GenericUtils.accumulateException(err, e);
-                }
-            }
-        }
-
-        if (err != null) {
-            throw err;
-        }
-    }
-
-    @Override
-    protected synchronized Closeable getInnerCloseable() {
-        return builder().parallel(dynamicLocal.values()).close(acceptor).build();
-    }
-
-    @Override
-    protected void preClose() {
-        this.listeners.clear();
-        this.managersHolder.clear();
-        super.preClose();
-    }
-
-    /**
-     * @param address        The request bind address
-     * @param handlerFactory A {@link Factory} to create an {@link IoHandler} if necessary
-     * @return The {@link InetSocketAddress} to which the binding occurred
-     * @throws IOException If failed to bind
-     */
-    private InetSocketAddress doBind(SshdSocketAddress address, Factory<? extends IoHandler> handlerFactory) throws IOException {
-        if (acceptor == null) {
-            Session session = getSession();
-            FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-            IoServiceFactory factory = Objects.requireNonNull(manager.getIoServiceFactory(), "No I/O service factory");
-            IoHandler handler = handlerFactory.create();
-            acceptor = factory.createAcceptor(handler);
-        }
-
-        // TODO find a better way to determine the resulting bind address - what if multi-threaded calls...
-        Set<SocketAddress> before = acceptor.getBoundAddresses();
-        try {
-            InetSocketAddress bindAddress = address.toInetSocketAddress();
-            acceptor.bind(bindAddress);
-
-            Set<SocketAddress> after = acceptor.getBoundAddresses();
-            if (GenericUtils.size(after) > 0) {
-                after.removeAll(before);
-            }
-            if (GenericUtils.isEmpty(after)) {
-                throw new IOException("Error binding to " + address + "[" + bindAddress + "]: no local addresses bound");
-            }
-
-            if (after.size() > 1) {
-                throw new IOException("Multiple local addresses have been bound for " + address + "[" + bindAddress + "]");
-            }
-            return (InetSocketAddress) after.iterator().next();
-        } catch (IOException bindErr) {
-            Set<SocketAddress> after = acceptor.getBoundAddresses();
-            if (GenericUtils.isEmpty(after)) {
-                close();
-            }
-            throw bindErr;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "[" + getSession() + "]";
-    }
-
-    //
-    // Static IoHandler implementation
-    //
-
-    class StaticIoHandler implements IoHandler {
-        StaticIoHandler() {
-            super();
-        }
-
-        @Override
-        @SuppressWarnings("synthetic-access")
-        public void sessionCreated(final IoSession session) throws Exception {
-            InetSocketAddress local = (InetSocketAddress) session.getLocalAddress();
-            int localPort = local.getPort();
-            SshdSocketAddress remote = localToRemote.get(localPort);
-            if (log.isDebugEnabled()) {
-                log.debug("sessionCreated({}) remote={}", session, remote);
-            }
-
-            final TcpipClientChannel channel;
-            if (remote != null) {
-                channel = new TcpipClientChannel(TcpipClientChannel.Type.Direct, session, remote);
-            } else {
-                channel = new TcpipClientChannel(TcpipClientChannel.Type.Forwarded, session, null);
-            }
-            session.setAttribute(TcpipClientChannel.class, channel);
-
-            service.registerChannel(channel);
-            channel.open().addListener(future -> {
-                Throwable t = future.getException();
-                if (t != null) {
-                    log.warn("Failed ({}) to open channel for session={}: {}",
-                             t.getClass().getSimpleName(), session, t.getMessage());
-                    if (log.isDebugEnabled()) {
-                        log.debug("sessionCreated(" + session + ") channel=" + channel + " open failure details", t);
-                    }
-                    DefaultTcpipForwarder.this.service.unregisterChannel(channel);
-                    channel.close(false);
-                }
-            });
-        }
-
-        @Override
-        @SuppressWarnings("synthetic-access")
-        public void sessionClosed(IoSession session) throws Exception {
-            TcpipClientChannel channel = (TcpipClientChannel) session.getAttribute(TcpipClientChannel.class);
-            if (channel != null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("sessionClosed({}) closing channel={}", session, channel);
-                }
-                channel.close(false);
-            }
-        }
-
-        @Override
-        @SuppressWarnings("synthetic-access")
-        public void messageReceived(IoSession session, Readable message) throws Exception {
-            TcpipClientChannel channel = (TcpipClientChannel) session.getAttribute(TcpipClientChannel.class);
-            Buffer buffer = new ByteArrayBuffer(message.available() + Long.SIZE, false);
-            buffer.putBuffer(message);
-
-            Collection<ClientChannelEvent> result = channel.waitFor(STATIC_IO_MSG_RECEIVED_EVENTS, Long.MAX_VALUE);
-            if (log.isTraceEnabled()) {
-                log.trace("messageReceived({}) channel={}, len={} wait result: {}",
-                          session, channel, result, buffer.array());
-            }
-
-            OutputStream outputStream = channel.getInvertedIn();
-            outputStream.write(buffer.array(), buffer.rpos(), buffer.available());
-            outputStream.flush();
-        }
-
-        @Override
-        @SuppressWarnings("synthetic-access")
-        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
-            if (log.isDebugEnabled()) {
-                log.debug("exceptionCaught({}) {}: {}", session, cause.getClass().getSimpleName(), cause.getMessage());
-            }
-            if (log.isTraceEnabled()) {
-                log.trace("exceptionCaught(" + session + ") caught exception details", cause);
-            }
-            session.close(false);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
deleted file mode 100644
index 808f41d..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarderFactory.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.forward;
-
-import java.util.Collection;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-import org.apache.sshd.common.session.ConnectionService;
-import org.apache.sshd.common.util.EventListenerUtils;
-
-/**
- * The default {@link TcpipForwarderFactory} implementation.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-public class DefaultTcpipForwarderFactory implements TcpipForwarderFactory, PortForwardingEventListenerManager {
-    public static final DefaultTcpipForwarderFactory INSTANCE = new DefaultTcpipForwarderFactory() {
-        @Override
-        public void addPortForwardingEventListener(PortForwardingEventListener listener) {
-            throw new UnsupportedOperationException("addPortForwardingListener(" + listener + ") N/A on default instance");
-        }
-
-        @Override
-        public void removePortForwardingEventListener(PortForwardingEventListener listener) {
-            throw new UnsupportedOperationException("removePortForwardingEventListener(" + listener + ") N/A on default instance");
-        }
-
-        @Override
-        public PortForwardingEventListener getPortForwardingEventListenerProxy() {
-            return PortForwardingEventListener.EMPTY;
-        }
-    };
-
-    private final Collection<PortForwardingEventListener> listeners = new CopyOnWriteArraySet<>();
-    private final PortForwardingEventListener listenerProxy;
-
-    public DefaultTcpipForwarderFactory() {
-        listenerProxy = EventListenerUtils.proxyWrapper(PortForwardingEventListener.class, getClass().getClassLoader(), listeners);
-    }
-
-    @Override
-    public PortForwardingEventListener getPortForwardingEventListenerProxy() {
-        return listenerProxy;
-    }
-
-    @Override
-    public void addPortForwardingEventListener(PortForwardingEventListener listener) {
-        listeners.add(PortForwardingEventListener.validateListener(listener));
-    }
-
-    @Override
-    public void removePortForwardingEventListener(PortForwardingEventListener listener) {
-        if (listener == null) {
-            return;
-        }
-
-        listeners.remove(PortForwardingEventListener.validateListener(listener));
-    }
-
-    @Override
-    public TcpipForwarder create(ConnectionService service) {
-        TcpipForwarder forwarder = new DefaultTcpipForwarder(service);
-        forwarder.addPortForwardingEventListenerManager(this);
-        return forwarder;
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilter.java
new file mode 100644
index 0000000..5c3583b
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.forward;
+
+
+import java.io.IOException;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface ForwardingFilter
+        extends PortForwardingManager,
+                PortForwardingEventListenerManager,
+                PortForwardingEventListenerManagerHolder,
+                Closeable {
+    /**
+     * @param remotePort The remote port
+     * @return The local {@link SshdSocketAddress} that the remote port is forwarded to
+     */
+    SshdSocketAddress getForwardedPort(int remotePort);
+
+    /**
+     * Called when the other side requested a remote port forward.
+     *
+     * @param local The request address
+     * @return The bound local {@link SshdSocketAddress} - {@code null} if not allowed to forward
+     * @throws IOException If failed to handle request
+     */
+    SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException;
+
+    /**
+     * Called when the other side cancelled a remote port forward.
+     *
+     * @param local The local {@link SshdSocketAddress}
+     * @throws IOException If failed to handle request
+     */
+    void localPortForwardingCancelled(SshdSocketAddress local) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilterFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilterFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilterFactory.java
new file mode 100644
index 0000000..485cbe7
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/ForwardingFilterFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.forward;
+
+import org.apache.sshd.common.session.ConnectionService;
+
+/**
+ * A factory for creating forwarder objects for client port forwarding
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface ForwardingFilterFactory {
+
+    /**
+     * Creates the forwarder to be used for TCP/IP port forwards for this session.
+     *
+     * @param service the {@link ConnectionService} the connections are forwarded through
+     * @return the {@link ForwardingFilter} that will listen for connections and set up forwarding
+     */
+    ForwardingFilter create(ConnectionService service);
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarder.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarder.java
deleted file mode 100644
index 5e3d9ce..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.forward;
-
-
-import java.io.IOException;
-
-import org.apache.sshd.common.Closeable;
-import org.apache.sshd.common.util.net.SshdSocketAddress;
-
-public interface TcpipForwarder
-        extends PortForwardingManager,
-                PortForwardingEventListenerManager,
-                PortForwardingEventListenerManagerHolder,
-                Closeable {
-    /**
-     * @param remotePort The remote port
-     * @return The local {@link SshdSocketAddress} that the remote port is forwarded to
-     */
-    SshdSocketAddress getForwardedPort(int remotePort);
-
-    /**
-     * Called when the other side requested a remote port forward.
-     *
-     * @param local The request address
-     * @return The bound local {@link SshdSocketAddress} - {@code null} if not allowed to forward
-     * @throws IOException If failed to handle request
-     */
-    SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException;
-
-    /**
-     * Called when the other side cancelled a remote port forward.
-     *
-     * @param local The local {@link SshdSocketAddress}
-     * @throws IOException If failed to handle request
-     */
-    void localPortForwardingCancelled(SshdSocketAddress local) throws IOException;
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarderFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarderFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarderFactory.java
deleted file mode 100644
index e001ab2..0000000
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/TcpipForwarderFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.forward;
-
-import org.apache.sshd.common.session.ConnectionService;
-
-/**
- * A factory for creating forwarder objects for client port forwarding
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface TcpipForwarderFactory {
-
-    /**
-     * Creates the forwarder to be used for TCP/IP port forwards for this session.
-     *
-     * @param service the {@link ConnectionService} the connections are forwarded through
-     * @return the {@link TcpipForwarder} that will listen for connections and set up forwarding
-     */
-    TcpipForwarder create(ConnectionService service);
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
index f4d584e..93175cf 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
@@ -42,8 +42,8 @@ import org.apache.sshd.common.channel.ChannelListener;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.config.VersionProperties;
 import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.forward.ForwardingFilterFactory;
 import org.apache.sshd.common.forward.PortForwardingEventListener;
-import org.apache.sshd.common.forward.TcpipForwarderFactory;
 import org.apache.sshd.common.io.DefaultIoServiceFactoryFactory;
 import org.apache.sshd.common.io.IoServiceFactory;
 import org.apache.sshd.common.io.IoServiceFactoryFactory;
@@ -70,8 +70,8 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     protected SshAgentFactory agentFactory;
     protected ScheduledExecutorService executor;
     protected boolean shutdownExecutor;
-    protected TcpipForwarderFactory tcpipForwarderFactory;
-    protected ForwardingFilter tcpipForwardingFilter;
+    protected ForwardingFilterFactory forwarderFactory;
+    protected ForwardingFilter forwardingFilter;
     protected FileSystemFactory fileSystemFactory;
     protected List<ServiceFactory> serviceFactories;
     protected List<RequestHandler<ConnectionService>> globalRequestHandlers;
@@ -217,21 +217,21 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
     }
 
     @Override
-    public TcpipForwarderFactory getTcpipForwarderFactory() {
-        return tcpipForwarderFactory;
+    public ForwardingFilterFactory getForwarderFactory() {
+        return forwarderFactory;
     }
 
-    public void setTcpipForwarderFactory(TcpipForwarderFactory tcpipForwarderFactory) {
-        this.tcpipForwarderFactory = tcpipForwarderFactory;
+    public void setForwarderFactory(ForwardingFilterFactory forwarderFactory) {
+        this.forwarderFactory = forwarderFactory;
     }
 
     @Override
-    public ForwardingFilter getTcpipForwardingFilter() {
-        return tcpipForwardingFilter;
+    public ForwardingFilter getForwardingFilter() {
+        return forwardingFilter;
     }
 
-    public void setTcpipForwardingFilter(ForwardingFilter tcpipForwardingFilter) {
-        this.tcpipForwardingFilter = tcpipForwardingFilter;
+    public void setForwardingFilter(ForwardingFilter forwardingFilter) {
+        this.forwardingFilter = forwardingFilter;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
index 9795e5c..7c4aa22 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/ConnectionService.java
@@ -23,9 +23,9 @@ import java.io.IOException;
 import org.apache.sshd.agent.common.AgentForwardSupport;
 import org.apache.sshd.common.Service;
 import org.apache.sshd.common.channel.Channel;
+import org.apache.sshd.common.forward.ForwardingFilter;
 import org.apache.sshd.common.forward.PortForwardingEventListenerManager;
 import org.apache.sshd.common.forward.PortForwardingEventListenerManagerHolder;
-import org.apache.sshd.common.forward.TcpipForwarder;
 import org.apache.sshd.server.x11.X11ForwardSupport;
 
 /**
@@ -51,11 +51,11 @@ public interface ConnectionService extends Service, PortForwardingEventListenerM
     void unregisterChannel(Channel channel);
 
     /**
-     * Retrieve the tcpip forwarder
+     * Retrieve the forwarder instance
      *
-     * @return The {@link TcpipForwarder}
+     * @return The {@link ForwardingFilter}
      */
-    TcpipForwarder getTcpipForwarder();
+    ForwardingFilter getForwardingFilter();
 
     // TODO: remove from interface, it's server side only
     AgentForwardSupport getAgentForwardSupport();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
index e3c8ecc..32d0920 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractConnectionService.java
@@ -45,10 +45,10 @@ import org.apache.sshd.common.channel.Channel;
 import org.apache.sshd.common.channel.OpenChannelException;
 import org.apache.sshd.common.channel.RequestHandler;
 import org.apache.sshd.common.channel.Window;
+import org.apache.sshd.common.forward.ForwardingFilter;
+import org.apache.sshd.common.forward.ForwardingFilterFactory;
 import org.apache.sshd.common.forward.PortForwardingEventListener;
 import org.apache.sshd.common.forward.PortForwardingEventListenerManager;
-import org.apache.sshd.common.forward.TcpipForwarder;
-import org.apache.sshd.common.forward.TcpipForwarderFactory;
 import org.apache.sshd.common.io.AbstractIoWriteFuture;
 import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.ConnectionService;
@@ -99,7 +99,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
 
     private final AtomicReference<AgentForwardSupport> agentForwardHolder = new AtomicReference<>();
     private final AtomicReference<X11ForwardSupport> x11ForwardHolder = new AtomicReference<>();
-    private final AtomicReference<TcpipForwarder> tcpipForwarderHolder = new AtomicReference<>();
+    private final AtomicReference<ForwardingFilter> forwarderHolder = new AtomicReference<>();
     private final AtomicBoolean allowMoreSessions = new AtomicBoolean(true);
     private final Collection<PortForwardingEventListener> listeners = new CopyOnWriteArraySet<>();
     private final Collection<PortForwardingEventListenerManager> managersHolder = new CopyOnWriteArraySet<>();
@@ -164,21 +164,21 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     }
 
     @Override
-    public TcpipForwarder getTcpipForwarder() {
-        TcpipForwarder forwarder;
+    public ForwardingFilter getForwardingFilter() {
+        ForwardingFilter forwarder;
         S session = getSession();
-        synchronized (tcpipForwarderHolder) {
-            forwarder = tcpipForwarderHolder.get();
+        synchronized (forwarderHolder) {
+            forwarder = forwarderHolder.get();
             if (forwarder != null) {
                 return forwarder;
             }
 
-            forwarder = ValidateUtils.checkNotNull(createTcpipForwarder(session), "No forwarder created for %s", session);
-            tcpipForwarderHolder.set(forwarder);
+            forwarder = ValidateUtils.checkNotNull(createForwardingFilter(session), "No forwarder created for %s", session);
+            forwarderHolder.set(forwarder);
         }
 
         if (log.isDebugEnabled()) {
-            log.debug("getTcpipForwarder({}) created instance", session);
+            log.debug("getForwardingFilter({}) created instance", session);
         }
         return forwarder;
     }
@@ -190,12 +190,12 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
         super.preClose();
     }
 
-    protected TcpipForwarder createTcpipForwarder(S session) {
+    protected ForwardingFilter createForwardingFilter(S session) {
         FactoryManager manager =
-                Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        TcpipForwarderFactory factory =
-                Objects.requireNonNull(manager.getTcpipForwarderFactory(), "No forwarder factory");
-        TcpipForwarder forwarder = factory.create(this);
+            Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
+        ForwardingFilterFactory factory =
+            Objects.requireNonNull(manager.getForwarderFactory(), "No forwarder factory");
+        ForwardingFilter forwarder = factory.create(this);
         forwarder.addPortForwardingEventListenerManager(this);
         return forwarder;
     }
@@ -252,7 +252,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession>
     @Override
     protected Closeable getInnerCloseable() {
         return builder()
-                .sequential(tcpipForwarderHolder.get(), agentForwardHolder.get(), x11ForwardHolder.get())
+                .sequential(forwarderHolder.get(), agentForwardHolder.get(), x11ForwardHolder.get())
                 .parallel(channels.values())
                 .build();
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index c9f348e..3e7c523 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -44,7 +44,6 @@ import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.ServiceFactory;
-import org.apache.sshd.common.config.SshConfigFileReader;
 import org.apache.sshd.common.config.keys.BuiltinIdentities;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.helpers.AbstractFactoryManager;
@@ -66,7 +65,7 @@ import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator;
 import org.apache.sshd.server.auth.password.PasswordAuthenticator;
 import org.apache.sshd.server.auth.pubkey.AcceptAllPublickeyAuthenticator;
 import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
-import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
+import org.apache.sshd.server.config.SshServerConfigFileReader;
 import org.apache.sshd.server.forward.ForwardingFilter;
 import org.apache.sshd.server.keyprovider.AbstractGeneratorHostKeyProvider;
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
@@ -264,11 +263,6 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
     }
 
     @Override
-    public void setTcpipForwardingFilter(ForwardingFilter forwardingFilter) {
-        this.tcpipForwardingFilter = forwardingFilter;
-    }
-
-    @Override
     protected void checkConfig() {
         super.checkConfig();
 
@@ -475,14 +469,14 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         for (int i = 0; i < numArgs; i++) {
             String argName = args[i];
             if ("-p".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires an argument: " + argName);
                     error = true;
                     break;
                 }
                 port = Integer.parseInt(args[++i]);
             } else if ("-key-type".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires an argument: " + argName);
                     error = true;
                     break;
@@ -495,7 +489,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
                 }
                 hostKeyType = args[++i].toUpperCase();
             } else if ("-key-size".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires an argument: " + argName);
                     error = true;
                     break;
@@ -509,7 +503,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
 
                 hostKeySize = Integer.parseInt(args[++i]);
             } else if ("-key-file".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires an argument: " + argName);
                     error = true;
                     break;
@@ -521,7 +515,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
                 }
                 keyFiles.add(keyFilePath);
             } else if ("-io".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires an argument: " + argName);
                     error = true;
                     break;
@@ -537,7 +531,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
                     break;
                 }
             } else if ("-o".equals(argName)) {
-                if (i + 1 >= numArgs) {
+                if ((i + 1) >= numArgs) {
                     System.err.println("option requires and argument: " + argName);
                     error = true;
                     break;
@@ -580,7 +574,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE);
         sshd.setPasswordAuthenticator((username, password, session) -> Objects.equals(username, password));
         sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-        sshd.setTcpipForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
+        setupServerForwarding(sshd, options);
         sshd.setCommandFactory(new ScpCommandFactory.Builder().withDelegate(
             command -> new ProcessShellFactory(GenericUtils.split(command, ' ')).create()
         ).build());
@@ -590,34 +584,14 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa
         Thread.sleep(Long.MAX_VALUE);
     }
 
-    public static Object setupServerBanner(ServerFactoryManager server, Map<String, ?> options) throws Exception {
-        String bannerOption = GenericUtils.isEmpty(options)
-                ? null
-                : Objects.toString(options.remove(SshConfigFileReader.BANNER_CONFIG_PROP), null);
-        if (GenericUtils.isEmpty(bannerOption)) {
-            bannerOption = GenericUtils.isEmpty(options)
-                    ? null
-                    : Objects.toString(options.remove(SshConfigFileReader.VISUAL_HOST_KEY), null);
-            if (SshConfigFileReader.parseBooleanValue(bannerOption)) {
-                bannerOption = ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE;
-            }
-        }
-
-        Object banner;
-        if (GenericUtils.isNotEmpty(bannerOption)) {
-            if ("none".equals(bannerOption)) {
-                return null;
-            }
-
-            if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(bannerOption)) {
-                banner = bannerOption;
-            } else {
-                banner = Paths.get(bannerOption);
-            }
-        } else {
-            banner = "Welcome to SSHD\n";
-        }
+    public static ForwardingFilter setupServerForwarding(SshServer server, Map<String, ?> options) {
+        ForwardingFilter forwardFilter = SshServerConfigFileReader.resolveServerForwarding(options);
+        server.setForwardingFilter(forwardFilter);
+        return forwardFilter;
+    }
 
+    public static Object setupServerBanner(ServerFactoryManager server, Map<String, ?> options) {
+        Object banner = SshServerConfigFileReader.resolveBanner(options);
         PropertyResolverUtils.updateProperty(server, ServerAuthenticationManager.WELCOME_BANNER, banner);
         return banner;
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index 053d2bc..44a5087 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -71,7 +71,8 @@ import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.SessionAware;
 import org.apache.sshd.server.Signal;
 import org.apache.sshd.server.StandardEnvironment;
-import org.apache.sshd.server.forward.ForwardingFilter;
+import org.apache.sshd.server.forward.AgentForwardingFilter;
+import org.apache.sshd.server.forward.X11ForwardingFilter;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.x11.X11ForwardSupport;
 
@@ -700,7 +701,7 @@ public class ChannelSession extends AbstractServerChannel {
         ServerSession session = getServerSession();
         PropertyResolverUtils.updateProperty(session, FactoryManager.AGENT_FORWARDING_TYPE, requestType);
         FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No session factory manager");
-        ForwardingFilter filter = manager.getTcpipForwardingFilter();
+        AgentForwardingFilter filter = manager.getAgentForwardingFilter();
         SshAgentFactory factory = manager.getAgentFactory();
         try {
             if ((factory == null) || (filter == null) || (!filter.canForwardAgent(session, requestType))) {
@@ -739,7 +740,7 @@ public class ChannelSession extends AbstractServerChannel {
         int screenId = buffer.getInt();
 
         FactoryManager manager = Objects.requireNonNull(session.getFactoryManager(), "No factory manager");
-        ForwardingFilter filter = manager.getTcpipForwardingFilter();
+        X11ForwardingFilter filter = manager.getX11ForwardingFilter();
         try {
             if ((filter == null) || (!filter.canForwardX11(session, requestType))) {
                 if (log.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/config/AllowTcpForwardingValue.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/AllowTcpForwardingValue.java b/sshd-core/src/main/java/org/apache/sshd/server/config/AllowTcpForwardingValue.java
new file mode 100644
index 0000000..ed6d7f8
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/AllowTcpForwardingValue.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.server.config;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.server.forward.TcpForwardingFilter;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <A HREF="http://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5">sshd_config(5) section</A>
+ */
+public enum AllowTcpForwardingValue implements TcpForwardingFilter {
+    ALL {
+        @Override
+        public boolean canListen(SshdSocketAddress address, Session session) {
+            return true;
+        }
+
+        @Override
+        public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+            return true;
+        }
+    },
+    NONE {
+        @Override
+        public boolean canListen(SshdSocketAddress address, Session session) {
+            return false;
+        }
+
+        @Override
+        public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+            return false;
+        }
+    },
+    LOCAL {
+        @Override
+        public boolean canListen(SshdSocketAddress address, Session session) {
+            return true;
+        }
+
+        @Override
+        public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+            return false;
+        }
+    },
+    REMOTE {
+        @Override
+        public boolean canListen(SshdSocketAddress address, Session session) {
+            return false;
+        }
+
+        @Override
+        public boolean canConnect(Type type, SshdSocketAddress address, Session session) {
+            return true;
+        }
+    };
+
+    public static final Set<AllowTcpForwardingValue> VALUES =
+            Collections.unmodifiableSet(EnumSet.allOf(AllowTcpForwardingValue.class));
+
+    // NOTE: it also interprets "yes" as "all" and "no" as "none"
+    public static AllowTcpForwardingValue fromString(String s) {
+        if (GenericUtils.isEmpty(s)) {
+            return null;
+        }
+
+        if ("yes".equalsIgnoreCase(s)) {
+            return ALL;
+        }
+
+        if ("no".equalsIgnoreCase(s)) {
+            return NONE;
+        }
+
+        for (AllowTcpForwardingValue v : VALUES) {
+            if (s.equalsIgnoreCase(v.name())) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
new file mode 100644
index 0000000..c5acd09
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.config;
+
+import java.nio.file.Paths;
+import java.util.Map;
+
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.server.ServerAuthenticationManager;
+import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
+import org.apache.sshd.server.forward.AgentForwardingFilter;
+import org.apache.sshd.server.forward.ForwardingFilter;
+import org.apache.sshd.server.forward.TcpForwardingFilter;
+import org.apache.sshd.server.forward.X11ForwardingFilter;
+
+/**
+ * Reads and interprets some useful configurations from an OpenSSH
+ * configuration file.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ * @see <a href="http://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5">sshd_config(5)</a>
+ */
+public final class SshServerConfigFileReader {
+    // Some well known configuration properties names and values
+    public static final String ALLOW_TCP_FORWARDING_CONFIG_PROP = "AllowTcpForwarding";
+    public static final String DEFAULT_TCP_FORWARDING = "yes";
+
+    public static final String ALLOW_AGENT_FORWARDING_CONFIG_PROP = "AllowAgentForwarding";
+    public static final String DEFAULT_AGENT_FORWARDING = "yes";
+
+    public static final String ALLOW_X11_FORWARDING_CONFIG_PROP = "X11Forwarding";
+    public static final String DEFAULT_X11_FORWARDING = "no";
+
+    public static final String BANNER_CONFIG_PROP = "Banner";
+
+    public static final String VISUAL_HOST_KEY = "VisualHostKey";
+    public static final String DEFAULT_VISUAL_HOST_KEY = "no";
+
+    private SshServerConfigFileReader() {
+        throw new UnsupportedOperationException("No instance allowed");
+    }
+
+    public static ForwardingFilter resolveServerForwarding(Map<String, ?> options) {
+        if (GenericUtils.isEmpty(options)) {
+            return AcceptAllForwardingFilter.INSTANCE;
+        }
+
+        AgentForwardingFilter agentFilter = resolveAgentForwardingFilter(options);
+        TcpForwardingFilter tcpFilter = resolveTcpForwardingFilter(options);
+        X11ForwardingFilter x11Filter = resolveX11ForwardingFilter(options);
+        return ForwardingFilter.asForwardingFilter(agentFilter, x11Filter, tcpFilter);
+    }
+
+    public static AgentForwardingFilter resolveAgentForwardingFilter(Map<String, ?> options) {
+        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING);
+        return AgentForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+    }
+
+    public static TcpForwardingFilter resolveTcpForwardingFilter(Map<String, ?> options) {
+        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING);
+        TcpForwardingFilter filter = AllowTcpForwardingValue.fromString(value);
+        ValidateUtils.checkNotNull(filter, "Unknown %s value: %s", ALLOW_TCP_FORWARDING_CONFIG_PROP, value);
+        return filter;
+    }
+
+    public static X11ForwardingFilter resolveX11ForwardingFilter(Map<String, ?> options) {
+        String value = PropertyResolverUtils.getStringProperty(options, ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING);
+        return X11ForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value));
+    }
+
+    public static Object resolveBanner(Map<String, ?> options) {
+        String bannerOption = PropertyResolverUtils.getString(options, BANNER_CONFIG_PROP);
+        if (GenericUtils.isEmpty(bannerOption)) {
+            bannerOption = PropertyResolverUtils.getStringProperty(options, VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY);
+            if (SshConfigFileReader.parseBooleanValue(bannerOption)) {
+                bannerOption = ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE;
+            } else {
+                bannerOption = null;
+            }
+        }
+
+        if (GenericUtils.isEmpty(bannerOption)) {
+            return "Welcome to SSHD\n";
+        } else if ("none".equals(bannerOption)) {
+            return null;
+        } else if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(bannerOption)) {
+            return bannerOption;
+        } else {
+            return Paths.get(bannerOption);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/aa551bc0/sshd-core/src/main/java/org/apache/sshd/server/forward/AgentForwardingFilter.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/AgentForwardingFilter.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/AgentForwardingFilter.java
new file mode 100644
index 0000000..97aaf12
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/AgentForwardingFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.server.forward;
+
+import org.apache.sshd.common.session.Session;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FunctionalInterface
+public interface AgentForwardingFilter {
+    // According to https://www.freebsd.org/cgi/man.cgi?query=sshd_config&sektion=5
+    AgentForwardingFilter DEFAULT = (session, requestType) -> true;
+
+    /**
+     * <p>
+     * Determine if the session may arrange for agent forwarding.
+     * </p>
+     *
+     * <p>
+     * This server process will open a new listen socket locally and export
+     * the address in the {@link org.apache.sshd.agent.SshAgent#SSH_AUTHSOCKET_ENV_NAME} environment
+     * variable.
+     * </p>
+     *
+     * @param session The {@link Session} requesting permission to forward the agent.
+     * @param requestType The request type string that triggered this call
+     * @return true if the agent forwarding is permitted, false if denied.
+     */
+    boolean canForwardAgent(Session session, String requestType);
+
+    static AgentForwardingFilter of(boolean enabled) {
+        return (session, requestType) -> enabled;
+    }
+}