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; + } +}