This commit is contained in:
Looly 2023-10-19 20:31:16 +08:00
parent 7f709f2730
commit 0f69a94d8c
6 changed files with 85 additions and 97 deletions

View File

@ -36,26 +36,24 @@ public interface Session extends Wrapper<Object>, Closeable {
/** /**
* 绑定端口到本地 一个会话可绑定多个端口<br> * 绑定端口到本地 一个会话可绑定多个端口<br>
* 当请求localHost:localPort时通过SSH到服务器转发请求到remoteHost:remotePort<br> * 当请求localHost:localPort时通过SSH到服务器转发请求到remoteHost:remotePort<br>
* 此方法用于访问本地无法访问但是服务器可以访问的地址内网数据库等 * 此方法用于访问本地无法访问但是服务器可以访问的地址只有服务器能访问的内网数据库等
* *
* @param localPort 本地端口 * @param localPort 本地端口
* @param remoteAddress 远程主机和端口 * @param remoteAddress 远程主机和端口
* @return 成功与否
*/ */
default boolean bindLocalPort(final int localPort, final InetSocketAddress remoteAddress) { default void bindLocalPort(final int localPort, final InetSocketAddress remoteAddress) {
return bindLocalPort(new InetSocketAddress(localPort), remoteAddress); bindLocalPort(new InetSocketAddress(localPort), remoteAddress);
} }
/** /**
* 绑定端口到本地 一个会话可绑定多个端口<br> * 绑定端口到本地 一个会话可绑定多个端口<br>
* 当请求localHost:localPort时通过SSH到服务器转发请求到remoteHost:remotePort<br> * 当请求localHost:localPort时通过SSH到服务器转发请求到remoteHost:remotePort<br>
* 此方法用于访问本地无法访问但是服务器可以访问的地址内网数据库等 * 此方法用于访问本地无法访问但是服务器可以访问的地址只有服务器能访问的内网数据库等
* *
* @param localAddress 本地主机和端口 * @param localAddress 本地主机和端口
* @param remoteAddress 远程主机和端口 * @param remoteAddress 远程主机和端口
* @return 成功与否
*/ */
boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress); void bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress);
/** /**
* 解除本地端口映射 * 解除本地端口映射
@ -73,5 +71,23 @@ public interface Session extends Wrapper<Object>, Closeable {
* @param localAddress 需要解除的本地地址 * @param localAddress 需要解除的本地地址
*/ */
void unBindLocalPort(final InetSocketAddress localAddress); void unBindLocalPort(final InetSocketAddress localAddress);
/**
* 绑定ssh服务端的serverPort端口, 到本地主机的port端口上. <br>
* 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上.<br>
* 此方法用于在服务端访问本地资源如服务器访问本机所在的数据库等
*
* @param remoteAddress ssh服务端上要被绑定的地址
* @param localAddress 转发到的本地地址
* @throws SshException 端口绑定失败异常
*/
void bindRemotePort(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress);
/**
* 解除远程端口映射
*
* @param remoteAddress 需要解除的远程地址和端口
*/
void unBindRemotePort(final InetSocketAddress remoteAddress);
// endregion // endregion
} }

View File

@ -18,7 +18,6 @@ import ch.ethz.ssh2.StreamGobbler;
import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.net.Ipv4Util;
import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.extra.ssh.Connector; import org.dromara.hutool.extra.ssh.Connector;
import org.dromara.hutool.extra.ssh.Session; import org.dromara.hutool.extra.ssh.Session;
@ -92,7 +91,7 @@ public class GanymedSession implements Session {
} }
@Override @Override
public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { public void bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException {
final LocalPortForwarder localPortForwarder; final LocalPortForwarder localPortForwarder;
try { try {
localPortForwarder = this.connection.createLocalPortForwarder(localAddress, remoteAddress.getHostName(), remoteAddress.getPort()); localPortForwarder = this.connection.createLocalPortForwarder(localAddress, remoteAddress.getHostName(), remoteAddress.getPort());
@ -106,8 +105,6 @@ public class GanymedSession implements Session {
//加入记录 //加入记录
this.localPortForwarderMap.put(localAddress.toString(), localPortForwarder); this.localPortForwarderMap.put(localAddress.toString(), localPortForwarder);
return true;
} }
@Override @Override
@ -126,34 +123,20 @@ public class GanymedSession implements Session {
} }
} }
/** @Override
* 绑定ssh服务端的serverPort端口, 到host主机的port端口上. <br> public void bindRemotePort(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws IORuntimeException {
* 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上.
*
* @param bindPort ssh服务端上要被绑定的端口
* @param host 转发到的host
* @param port host上的端口
* @return 成功与否
* @throws IORuntimeException 端口绑定失败异常
*/
public boolean bindRemotePort(final int bindPort, final String host, final int port) throws IORuntimeException {
try { try {
this.connection.requestRemotePortForwarding(Ipv4Util.LOCAL_IP, bindPort, host, port); this.connection.requestRemotePortForwarding(remoteAddress.getHostName(), remoteAddress.getPort(),
localAddress.getHostName(), localAddress.getPort());
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
return true;
} }
/** @Override
* 解除远程端口映射 public void unBindRemotePort(final InetSocketAddress remoteAddress) throws IORuntimeException {
*
* @param localPort 需要解除的本地端口
* @throws IORuntimeException 端口解绑失败异常
*/
public void unBindRemotePort(final int localPort) throws IORuntimeException {
try { try {
this.connection.cancelRemotePortForwarding(localPort); this.connection.cancelRemotePortForwarding(remoteAddress.getPort());
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }

View File

@ -74,16 +74,12 @@ public class JschSession implements Session {
} }
@Override @Override
public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws SshException { public void bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws SshException {
if (isConnected()) {
try { try {
this.raw.setPortForwardingL(localAddress.getHostName(), localAddress.getPort(), remoteAddress.getHostName(), remoteAddress.getPort()); this.raw.setPortForwardingL(localAddress.getHostName(), localAddress.getPort(), remoteAddress.getHostName(), remoteAddress.getPort());
} catch (final JSchException e) { } catch (final JSchException e) {
throw new SshException(e, "From [{}] mapping to [{}] error", localAddress, remoteAddress); throw new SshException(e, "From [{}] mapping to [{}] error", localAddress, remoteAddress);
} }
return true;
}
return false;
} }
@Override @Override
@ -95,36 +91,20 @@ public class JschSession implements Session {
} }
} }
/** @Override
* 绑定ssh服务端的serverPort端口, 到host主机的port端口上. <br> public void bindRemotePort(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws SshException {
* 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上.
*
* @param bindPort ssh服务端上要被绑定的端口
* @param host 转发到的host
* @param port host上的端口
* @return 成功与否
* @throws SshException 端口绑定失败异常
*/
public boolean bindRemotePort(final int bindPort, final String host, final int port) throws SshException {
if (isConnected()) {
try { try {
this.raw.setPortForwardingR(bindPort, host, port); this.raw.setPortForwardingR(remoteAddress.getHostName(), remoteAddress.getPort(),
localAddress.getHostName(), localAddress.getPort());
} catch (final JSchException e) { } catch (final JSchException e) {
throw new SshException(e, "From [{}] mapping to [{}] error", bindPort, port); throw new SshException(e, "From [{}] mapping to [{}] error", remoteAddress, localAddress);
} }
return true;
}
return false;
} }
/** @Override
* 解除远程端口映射 public void unBindRemotePort(final InetSocketAddress remoteAddress) {
*
* @param localPort 需要解除的本地端口
*/
public void unBindRemotePort(final int localPort) {
try { try {
this.raw.delPortForwardingR(localPort); this.raw.delPortForwardingR(remoteAddress.getHostName(), remoteAddress.getPort());
} catch (final JSchException e) { } catch (final JSchException e) {
throw new SshException(e); throw new SshException(e);
} }

View File

@ -66,13 +66,12 @@ public class MinaSession implements Session {
} }
@Override @Override
public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { public void bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException {
try { try {
this.raw.startLocalPortForwarding(new SshdSocketAddress(localAddress), new SshdSocketAddress(remoteAddress)); this.raw.startLocalPortForwarding(new SshdSocketAddress(localAddress), new SshdSocketAddress(remoteAddress));
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
return true;
} }
@Override @Override
@ -84,6 +83,24 @@ public class MinaSession implements Session {
} }
} }
@Override
public void bindRemotePort(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws IORuntimeException {
try {
this.raw.startRemotePortForwarding(new SshdSocketAddress(remoteAddress), new SshdSocketAddress(localAddress));
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
@Override
public void unBindRemotePort(final InetSocketAddress remoteAddress) throws IORuntimeException{
try {
this.raw.stopRemotePortForwarding(new SshdSocketAddress(remoteAddress));
} catch (final IOException e) {
throw new IORuntimeException(e);
}
}
/** /**
* 执行Shell命令 * 执行Shell命令
* *

View File

@ -19,6 +19,7 @@ import net.schmizz.sshj.connection.channel.forwarded.SocketForwardingConnectList
import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.extra.ssh.Connector; import org.dromara.hutool.extra.ssh.Connector;
import org.dromara.hutool.extra.ssh.Session; import org.dromara.hutool.extra.ssh.Session;
@ -39,7 +40,7 @@ import java.util.Set;
*/ */
public class SshjSession implements Session { public class SshjSession implements Session {
private SSHClient ssh; private final SSHClient ssh;
private final net.schmizz.sshj.connection.channel.direct.Session raw; private final net.schmizz.sshj.connection.channel.direct.Session raw;
private Map<String, ServerSocket> localPortForwarderMap; private Map<String, ServerSocket> localPortForwarderMap;
@ -94,7 +95,7 @@ public class SshjSession implements Session {
} }
@Override @Override
public boolean bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException { public void bindLocalPort(final InetSocketAddress localAddress, final InetSocketAddress remoteAddress) throws IORuntimeException {
final Parameters params = new Parameters( final Parameters params = new Parameters(
localAddress.getHostName(), localAddress.getPort(), localAddress.getHostName(), localAddress.getPort(),
remoteAddress.getHostName(), remoteAddress.getPort()); remoteAddress.getHostName(), remoteAddress.getPort());
@ -114,8 +115,6 @@ public class SshjSession implements Session {
//加入记录 //加入记录
this.localPortForwarderMap.put(localAddress.toString(), ss); this.localPortForwarderMap.put(localAddress.toString(), ss);
return true;
} }
@Override @Override
@ -127,38 +126,33 @@ public class SshjSession implements Session {
IoUtil.closeQuietly(this.localPortForwarderMap.remove(localAddress.toString())); IoUtil.closeQuietly(this.localPortForwarderMap.remove(localAddress.toString()));
} }
/** @Override
* 绑定ssh服务端的serverPort端口, 到host主机的port端口上. <br> public void bindRemotePort(final InetSocketAddress remoteAddress, final InetSocketAddress localAddress) throws IORuntimeException {
* 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上.
*
* @param bindPort ssh服务端上要被绑定的端口
* @param host 转发到的host
* @param port host上的端口
* @return 成功与否
* @throws IORuntimeException 端口绑定失败异常
*/
public boolean bindRemotePort(final int bindPort, final String host, final int port) throws IORuntimeException {
try { try {
this.ssh.getRemotePortForwarder().bind( this.ssh.getRemotePortForwarder().bind(
new RemotePortForwarder.Forward(bindPort), new RemotePortForwarder.Forward(remoteAddress.getHostName(), remoteAddress.getPort()),
new SocketForwardingConnectListener(new InetSocketAddress(host, port)) new SocketForwardingConnectListener(localAddress)
); );
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
} }
return true;
} }
/** @Override
* 解除远程端口映射 public void unBindRemotePort(final InetSocketAddress remoteAddress) {
* final String hostName = remoteAddress.getHostName();
* @param localPort 需要解除的本地端口 final int port = remoteAddress.getPort();
*/
public void unBindRemotePort(final int localPort) {
final RemotePortForwarder remotePortForwarder = this.ssh.getRemotePortForwarder(); final RemotePortForwarder remotePortForwarder = this.ssh.getRemotePortForwarder();
final Set<RemotePortForwarder.Forward> activeForwards = remotePortForwarder.getActiveForwards(); final Set<RemotePortForwarder.Forward> activeForwards = remotePortForwarder.getActiveForwards();
for (final RemotePortForwarder.Forward activeForward : activeForwards) { for (final RemotePortForwarder.Forward activeForward : activeForwards) {
if (localPort == activeForward.getPort()) { if (port == activeForward.getPort()) {
final String activeAddress = activeForward.getAddress();
if(StrUtil.isNotBlank(activeAddress) && !StrUtil.equalsIgnoreCase(hostName, activeAddress)){
// 对于用于已经定义的host做对比否则跳过
continue;
}
try { try {
remotePortForwarder.cancel(activeForward); remotePortForwarder.cancel(activeForward);
} catch (final IOException e) { } catch (final IOException e) {

View File

@ -17,7 +17,6 @@ import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.extra.ssh.engine.jsch.JschSession; import org.dromara.hutool.extra.ssh.engine.jsch.JschSession;
import org.dromara.hutool.extra.ssh.engine.jsch.JschSftp; import org.dromara.hutool.extra.ssh.engine.jsch.JschSftp;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -48,8 +47,7 @@ public class JschTest {
// 建立会话 // 建立会话
final JschSession session = new JschSession(new Connector("looly.centos", 22, "test", "123456")); final JschSession session = new JschSession(new Connector("looly.centos", 22, "test", "123456"));
// 绑定ssh服务端8089端口到本机的8000端口上 // 绑定ssh服务端8089端口到本机的8000端口上
final boolean b = session.bindRemotePort(8089, "localhost", 8000); session.bindRemotePort(new InetSocketAddress(8089), new InetSocketAddress("localhost", 8000));
Assertions.assertTrue(b);
// 保证一直运行 // 保证一直运行
} }