@Override public void handleOpenSuccess(int recipient, int rwSize, int packetSize, Buffer buffer) { setRecipient(recipient); Session session = getSession(); FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager"); this.remoteWindow.init(rwSize, packetSize, manager.getProperties()); ChannelListener listener = getChannelListenerProxy(); try { doOpen(); listener.channelOpenSuccess(this); this.opened.set(true); this.openFuture.setOpened(); } catch (Throwable t) { Throwable e = GenericUtils.peelException(t); try { listener.channelOpenFailure(this, e); } catch (Throwable ignored) { log.warn( "handleOpenSuccess({}) failed ({}) to inform listener of open failure={}: {}", this, ignored.getClass().getSimpleName(), e.getClass().getSimpleName(), ignored.getMessage()); } this.openFuture.setException(e); this.closeFuture.setClosed(); this.doCloseImmediately(); } finally { notifyStateChanged(); } }
@Override public synchronized OpenFuture open() throws IOException { if (isClosing()) { throw new SshException("Session has been closed"); } openFuture = new DefaultOpenFuture(lock); if (log.isDebugEnabled()) { log.debug("open({}) Send SSH_MSG_CHANNEL_OPEN - type={}", this, type); } Session session = getSession(); Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_CHANNEL_OPEN, type.length() + Integer.SIZE); buffer.putString(type); buffer.putInt(getId()); buffer.putInt(localWindow.getSize()); buffer.putInt(localWindow.getPacketSize()); writePacket(buffer); return openFuture; }
@Override public synchronized SshdSocketAddress startRemotePortForwarding( SshdSocketAddress remote, SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkNotNull(remote, "Remote address is null"); Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST); buffer.putString("tcpip-forward"); buffer.putBoolean(true); buffer.putString(remote.getHostName()); buffer.putInt(remote.getPort()); Buffer result = session.request(buffer); if (result == null) { throw new SshException("Tcpip forwarding request denied by server"); } int port = (remote.getPort() == 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); } SshdSocketAddress bound = new SshdSocketAddress(remote.getHostName(), port); if (log.isDebugEnabled()) { log.debug("startRemotePortForwarding(" + remote + " -> " + local + "): " + bound); } return bound; }
@Override public Result process( ConnectionService connectionService, String request, boolean wantReply, Buffer buffer) throws Exception { if (!REQUEST.equals(request)) { return super.process(connectionService, request, wantReply, buffer); } String address = buffer.getString(); int port = buffer.getInt(); SshdSocketAddress socketAddress = new SshdSocketAddress(address, port); TcpipForwarder forwarder = Objects.requireNonNull(connectionService.getTcpipForwarder(), "No TCP/IP forwarder"); SshdSocketAddress bound = forwarder.localPortForwardingRequested(socketAddress); if (log.isDebugEnabled()) { log.debug( "process({})[{}][want-reply-{}] {} => {}", connectionService, request, wantReply, socketAddress, bound); } if (bound == null) { return Result.ReplyFailure; } port = bound.getPort(); if (wantReply) { Session session = connectionService.getSession(); buffer = session.createBuffer(SshConstants.SSH_MSG_REQUEST_SUCCESS, Integer.BYTES); buffer.putInt(port); session.writePacket(buffer); } return Result.Replied; }
@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); } Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST); buffer.putString("cancel-tcpip-forward"); buffer.putBoolean(false); buffer.putString(remote.getHostName()); buffer.putInt(remote.getPort()); session.writePacket(buffer); } else { if (log.isDebugEnabled()) { log.debug("stopRemotePortForwarding(" + remote + ") no binding found"); } } }
@Override public synchronized SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local); FactoryManager manager = session.getFactoryManager(); ForwardingFilter filter = manager.getTcpipForwardingFilter(); if ((filter == null) || (!filter.canListen(local, session))) { if (log.isDebugEnabled()) { log.debug( "localPortForwardingRequested(" + session + ")[" + local + "][haveFilter=" + (filter != null) + "] rejected"); } throw new IOException("Rejected address: " + local); } InetSocketAddress bound = doBind(local, staticIoHandlerFactory); SshdSocketAddress 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); } return result; }
@Override public FileSystem createFileSystem(Session session) { String userName = session.getUsername(); // create home if does not exist if (createHome) { String homeDirStr = "/home/" + userName; File homeDir = new File(homeDirStr); if (homeDir.isFile()) { log.warn("Not a directory :: " + homeDirStr); // throw new FtpException("Not a directory :: " + homeDirStr); } if ((!homeDir.exists()) && (!homeDir.mkdirs())) { log.warn("Cannot create user home :: " + homeDirStr); // throw new FtpException("Cannot create user home :: " // + homeDirStr); } } return FileSystems.getDefault(); }
/** * @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) { FactoryManager manager = session.getFactoryManager(); IoServiceFactory factory = manager.getIoServiceFactory(); 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; } }