You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中实现SSH动态端口转发,自动化跨服务器访问?

实现Rust版SSH动态端口转发(SOCKS5)

用ssh2-rs实现(基于libssh2)

ssh2-rs其实支持动态端口转发(对应ssh -D的SOCKS5代理功能),只是需要手动处理本地监听和连接转发逻辑。

步骤:

  1. 添加依赖到Cargo.toml:
[dependencies]
ssh2 = "0.10"
tokio = { version = "1.0", features = ["full"] }

(用tokio做异步IO,方便处理多客户端连接)

  1. 核心实现代码:
use std::net::{TcpListener, TcpStream};
use ssh2::Session;
use tokio::task;

async fn handle_client(mut local_stream: TcpStream, session: Session) {
    // 读取SOCKS5握手请求
    let mut handshake_buf = [0u8; 1024];
    let n = local_stream.read(&mut handshake_buf).unwrap();
    // 回复SOCKS5确认(无认证)
    local_stream.write_all(&[0x05, 0x00]).unwrap();

    // 读取目标地址请求
    let n = local_stream.read(&mut handshake_buf).unwrap();
    let addr_type = handshake_buf[3];
    let (target_host, target_port) = match addr_type {
        0x01 => { // IPv4
            let host = format!(
                "{}.{}.{}.{}",
                handshake_buf[4], handshake_buf[5], handshake_buf[6], handshake_buf[7]
            );
            let port = ((handshake_buf[8] as u16) << 8) | handshake_buf[9] as u16;
            (host, port)
        }
        0x03 => { // 域名
            let len = handshake_buf[4] as usize;
            let host = String::from_utf8_lossy(&handshake_buf[5..5+len]).to_string();
            let port = ((handshake_buf[5+len] as u16) << 8) | handshake_buf[5+len+1] as u16;
            (host, port)
        }
        _ => panic!("Unsupported address type"),
    };

    // 通过SSH会话建立到目标服务器的连接
    let mut remote_stream = session.channel_direct_tcpip(&target_host, target_port).unwrap();

    // 回复SOCKS5连接成功
    local_stream.write_all(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).unwrap();

    // 双向转发数据
    task::spawn(async move {
        let _ = std::io::copy(&mut local_stream, &mut remote_stream);
        remote_stream.close().unwrap();
    });
    let _ = std::io::copy(&mut remote_stream, &mut local_stream);
    local_stream.shutdown(std::net::Shutdown::Both).unwrap();
}

#[tokio::main]
async fn main() {
    // 建立SSH连接到中转服务器B
    let tcp_stream = TcpStream::connect("computer_b_ip:22").unwrap();
    let mut sess = Session::new().unwrap();
    sess.set_tcp_stream(tcp_stream);
    sess.handshake().unwrap();
    sess.userauth_password("myUser", "myPassword").unwrap(); // 也可以替换为密钥认证逻辑
    assert!(sess.authenticated());

    // 监听本地端口1080
    let listener = TcpListener::bind("127.0.0.1:1080").unwrap();
    println!("SOCKS5 proxy listening on 127.0.0.1:1080");

    // 处理每个客户端连接
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let sess_clone = sess.clone();
                task::spawn(async move {
                    handle_client(stream, sess_clone).await;
                });
            }
            Err(e) => println!("Failed to accept connection: {}", e),
        }
    }
}

说明:

  • 这段代码实现了完整的SOCKS5代理逻辑,完全对应ssh -D 1080 myUser@ComputerB的功能
  • 支持IPv4和域名类型的目标地址,处理双向数据转发
  • 用tokio实现异步处理多客户端连接,避免单连接阻塞整个代理

用thrussh实现(纯Rust)

thrussh是纯Rust编写的SSH库,没有直接封装SOCKS5逻辑,但可以通过底层API手动实现:

  1. 添加依赖:
[dependencies]
thrussh = "0.43"
thrussh-keys = "0.22"
tokio = { version = "1.0", features = ["full"] }
  1. 核心思路:
  • 建立thrussh的SSH客户端连接(支持密码或密钥认证)
  • 监听本地1080端口,复用上面的SOCKS5握手逻辑
  • 针对每个目标地址,调用thrussh的channel_open_direct_tcpip方法建立远程连接
  • 启动异步任务双向转发本地流与远程SSH通道的数据

构建单二进制文件

要生成可直接分享的单二进制文件,在Cargo.toml中添加以下发布配置:

[profile.release]
strip = true
opt-level = 3
lto = true
codegen-units = 1

执行cargo build --release后,可执行文件会生成在target/release目录下。Windows平台需确保编译时静态链接依赖库,建议使用GCC工具链编译。

内容的提问来源于stack exchange,提问作者lolozen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 16:34:53