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 "tcpip-forward" action
- *
- * @author <a href="mailto:d...@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:d...@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:d...@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:d...@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:d...@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:d...@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:d...@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:d...@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;
+    }
+}

Reply via email to