ssh_client

SSHv2 client library for MoonBit with password and publickey authentication, SFTP v3, and local/remote/SOCKS5 port forwarding. / 使用 MoonBit 实现的 SSHv2 客户端库,支持密码/公钥认证、SFTP v3 文件传输及本地/远程/SOCKS5 端口转发

ssh
sshv2
ssh-client
sftp
port-forwarding
socks5
publickey-auth
password-auth
ffi
moon add PaiGack/ssh_client@0.4.0
Download zip
Author
Version
0.4.0
License
Apache-2.0
Last updated
last month
Downloads
45
README

#MoonSSH — MoonBit SSH 客户端库

使用 MoonBit 实现的 SSHv2 客户端库

  • 协议层完全 MoonBit 原创
  • 密码学原语通过 FFI 复用系统 OpenSSL libcrypto,不重复造轮子
  • TCP 传输层使用 socket FFI:
    • Windows:Winsock socket FFI(不依赖 MSVC)
    • POSIX(Linux / macOS):POSIX socket FFI

#1. 项目状态

能力状态说明
TCP 传输层src/socket/socket.mbt + socket.c(自带 socket FFI,支持 POSIX 与 Winsock2)
Banner 交换Client::connect()
包二进制编解码src/packet.mbt:length / padding / payload / MAC(EtM 流加密模式)
KEXINIT 协商src/kex.mbtecdh-sha2-nistp256 / diffie-hellman-group14-sha256 / diffie-hellman-group14-sha1
密钥派生RFC 4253 §7.2 派生(derive_keys()),HMAC-SHA-1/256 扩展
主机密钥验证ssh-rsa / rsa-sha2-256 / rsa-sha2-512 / ssh-dss / ecdsa-sha2-nistp256/384/521 / ssh-ed25519
密码认证Client::auth_password() 完整流程
公钥认证Client::auth_publickey()(two-step 查询 + 签名);私钥支持 RSA / Ed25519 / DSA / ECDSA;公钥文件必须是 OpenSSH 单行格式
keyboard-interactive 认证Client::auth_keyboard_interactive()
自动回退认证Client::auth_auto():none → publickey → keyboard-interactive → password
exec 命令执行Client::exec() 返回 (stdout, stderr)
shell 通道Client::shell()(不管理交互式 I/O,仅打开 shell 通道)
known_hosts 解析基础解析 + 通配符;HMAC-SHA1 哈希形式(\|1\|…)待支持
SFTPSFTP v3 协议实现(src/sftp.mbt
远程端口转发 (-R)Client::forward_remote_port()tcpip-forward 全局请求 + forwarded-tcpip 通道
本地端口转发 (-L)Client::forward_local_port()direct-tcpip 通道(双向 relay_data 中继)
SOCKS5 动态转发 (-D)Client::forward_socks5() — SOCKS5 代理

#2. 架构

┌─────────────────────────────────────────────────────────┐ │ cmd/{password,key,sftp,forwarding,output}/ │ │ ── CLI 入口(按认证方式/场景分命令)│ └────────────────────────┬────────────────────────────────┘ │ @src.Client / @src.SftpClient ┌────────────────────────▼────────────────────────────────┐ │ src/ssh_client.mbt ── 顶层 API │ │ • ConnectOptions ── 连接选项(含 verifier) │ │ • Client::connect ── Banner 交换 │ │ • Client::kex ── 密钥协商 │ │ • Client::auth_password ── 密码认证 │ │ • Client::auth_publickey── 公钥认证(two-step) │ │ • Client::auth_keyboard_interactive ── kbd-int 认证 │ │ • Client::auth_auto ── 自动回退认证 │ │ • Client::open_session ── 打开 session 通道 │ │ • Client::exec ── 命令执行(stdout,stderr) │ │ • Client::shell ── shell 通道 │ │ • Client::forward_local_port ── 本地转发 (-L) │ │ • Client::forward_remote_port ── 远程转发 (-R) │ │ • Client::forward_socks5 ── SOCKS5 代理 (-D) │ │ • Client::cancel_remote_forward ── 取消远程转发 │ │ • Client::close ── 清理 │ └────────────────────────┬────────────────────────────────┘ │ ┌─────────────────────┼─────────────────────┐ │ │ │ ┌──▼────────┐ ┌────────▼─────┐ ┌───────────▼──┐ │ packet.mbt│ │ kex.mbt │ │ auth.mbt │ │ • length │ │ • KexContext│ │ • AuthContext│ │ • padding│ │ • 状态机 │ │ • password │ │ • MAC │ │ • 算法协商 │ │ • publickey │ └─────┬─────┘ └──────┬──────┘ │ • kbd-int │ │ │ └──────┬───────┘ │ │ │ │ ┌──────────▼──────────┐ │ │ │ channel.mbt │ │ │ │ • session / exec │ │ │ │ • shell / pty-req │ │ │ │ • subsystem (sftp) │ │ │ │ • window adjust │ │ │ │ • stdout/stderr │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ sftp.mbt │ │ │ │ • SFTP v3 协议 │ │ │ │ • 文件传输 │ │ │ │ • 目录管理 │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ known_hosts.mbt │ │ │ │ • 通配符匹配 │ │ │ │ • base64 编解码 │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ log.mbt │ │ │ │ • debug / hex_dump │ │ │ └─────────────────────┘ │ │ │ ┌─────▼──────────────────────────────────▼───────────────┐ │ src/socket/* ── TCP 传输层(自实现 socket FFI) │ │ • socket.mbt ── Tcp::connect_to_host / write / … │ │ • socket.c ── Winsock2 / POSIX socket 实现 │ └────────────────────────┬────────────────────────────────┘ │ ┌────────────────────────▼────────────────────────────────┐ │ src/crypto/* ── 密码学原语(FFI 调 OpenSSL libcrypto)│ │ • digest / mac / cipher / pkey / kex / error │ │ • openssl.c ── dlopen libcrypto + EVP_* 包装 │ │ • openssl_loader.mbt / crypto_util.mbt │ └────────────────────────┬────────────────────────────────┘ │ FFI ┌────────────────────────▼────────────────────────────────┐ │ libcrypto.so.3 / libcrypto.dylib / libcrypto-3-x64.dll │ └─────────────────────────────────────────────────────────┘

#2.1 协议状态机

connect() │ ▼ [INIT] ───────► send_banner / recv_banner │ ▼ [KEXINIT] ◄──► KexContext::drive │ │ KEXDH_INIT / KEX_ECDH_INIT │ ▼ │ [KEXDH_REPLY / KEX_ECDH_REPLY] │ │ │ ▼ │ [NEWKEYS] ──► 启用新密钥 ▼ [AUTH] ◄──► AuthContext │ • none 探测 │ • password / publickey / kbd-int ▼ [CHANNEL] ◄──► Channel::open_session │ │ exec / shell │ ▼ │ [DATA + EXIT-STATUS] ▼ [CLOSE]

#3. 项目结构

moonbit-ssh-client/ ├── AGENTS.md 项目开发规范 ├── README.md ├── README.mbt.md mooncakes.io 简介 ├── LICENSE Apache-2.0 ├── moon.mod 模块清单 ├── moon.pkg 根包(仅导入子包) ├── ssh_client.mbt 根包占位 ├── ssh_client_test.mbt 黑盒测试 ├── ssh_client_wbtest.mbt 白盒测试 ├── pkg.generated.mbti 根包生成接口 ├── .cnb.yml CNB 开发环境配置(含 docker sshd 自启) ├── src/ ★ 核心库 │ ├── moon.pkg 子包:PaiGack/ssh_client/src │ ├── pkg.generated.mbti 生成接口 │ ├── log.mbt 调试输出(debug / hex_dump / 开关) │ ├── ssh_client.mbt 顶层 API(ConnectOptions / Client) │ ├── packet.mbt 包序列化(length/padding/payload/MAC)+ Reader/Writer │ ├── kex.mbt KEXINIT 状态机、密钥派生 │ ├── auth.mbt 用户认证(password / publickey / kbd-int / none) │ ├── channel.mbt 通道(session/exec/shell/pty-req/subsystem,状态机 + stdout/stderr) │ ├── sftp.mbt SFTP v3 协议(文件传输 / 目录管理) │ ├── known_hosts.mbt known_hosts 解析 + 通配符匹配 │ ├── socket/ ★ TCP 传输层(自实现 FFI) │ │ ├── moon.pkg │ │ ├── pkg.generated.mbti │ │ ├── socket.mbt Tcp::connect_to_host / write / read_* │ │ └── socket.c Winsock2 / POSIX socket 实现 + TCP_NODELAY │ └── crypto/ ★ 密码学子包 │ ├── moon.pkg │ ├── pkg.generated.mbti │ ├── openssl.c OpenSSL dlopen + EVP_* / BN_* 包装 │ ├── openssl_loader.mbt 动态加载 libcrypto │ ├── crypto_util.mbt RAND_bytes / ERR_* │ ├── digest.mbt SHA-1/256/384/512 │ ├── mac.mbt HMAC-SHA-1 / HMAC-SHA-256 │ ├── cipher.mbt AES-128-CTR │ ├── pkey.mbt RSA/Ed25519/DSA/ECDSA 签名/验证 + 加载 PEM/OpenSSH 私钥 │ ├── kex.mbt DH Group14 + BigInt FFI(BN_*) │ └── error.mbt CryptoError ├── cmd/ ★ CLI 入口(按认证方式/场景分多个子命令) │ ├── output/ Hello MoonBit 演示(println "Hello MoonBit!") │ ├── utils/ CMD args 工具:tokenize / split_user_host / parse_forward_arg / parse_int │ ├── password/ 密码认证(auth_auto) │ ├── key/ Ed25519/RSA/ECDSA 公钥认证 │ ├── sftp/ SFTP 文件传输客户端(ls/get/put/rm/mkdir/rmdir/stat) │ └── forwarding/ 端口转发(-L / -R / -D),三个独立 run-*.sh ├── docs/ │ ├── prd_000.md 设计 PRD │ ├── prd_001_ssh-key-types-support-plan.md 密钥类型扩展计划 │ ├── prd_002_sftp-support-plan.md SFTP 实现计划 │ ├── prd_003_port-forwarding-plan.md 端口转发设计文档 │ └── crypto-replacement-plan.md OpenSSL 替换为 MoonBit 原生实现的方案 └── scripts/ └── ssh-server/ 本地 docker sshd 脚本(每个认证方式独立) ├── .env 密码(password="123456") ├── .gitignore 忽略本地生成的密钥对 ├── password.sh 密码认证 sshd(端口 1022→2222) ├── key-ed25519.sh 生成 ed25519 密钥 + 公钥认证 sshd(端口 2022→2222) ├── key-rsa.sh 生成 4096 位 rsa 密钥 + 公钥认证 sshd(端口 3022→2222) ├── key-ecdsa.sh 生成 ecdsa 密钥 + 公钥认证 sshd(端口 4022→2222) └── forwarding.sh 密码认证 sshd(端口 5022→2222,AllowTcpForwarding=yes)+ nginx(1080) 验证目标

#4. 快速开始

#4.1 前置依赖

依赖版本说明
MoonBit toolchain≥ 0.19https://www.moonbitlang.com/
OpenSSL libcrypto1.1.1+ 或 3.x系统库(Linux/macOS 自带;Windows 用 MinGW OpenSSL)
Docker(可选)任意用于本地启动测试 sshd
OpenSSH 客户端(可选)任意调试用 ssh -i 验证 sshd

Windows 额外依赖 MinGW:

# add C:\msys64\ucrt64\bin to PATH pacman -S mingw-w64-ucrt-x86_64-gcc pacman -S mingw-w64-ucrt-x86_64-openssl

Linux
apt-get install -y gcc libssl-dev

macOS:
brew install gcc openssl

#4.2 启动测试 sshd

scripts/ssh-server/ 下提供本地 docker sshd 启动脚本(每个脚本独立监听端口):

脚本用途主机端口
password.sh启动 lscr.io/linuxserver/openssh-server(密码模式)1022
key-ed25519.sh生成 ed25519 密钥并启动公钥认证 sshd2022
key-rsa.sh生成 4096 位 RSA 密钥并启动公钥认证 sshd3022
key-ecdsa.sh生成 ecdsa 密钥并启动公钥认证 sshd4022
forwarding.sh启动密码认证 sshd 并开启端口转发5022

注意: 每次启动 key-*.sh 都会清空并重新生成同名密钥对(id_ed25519 / id_rsa / id_ecdsa);生成位置在 scripts/ssh-server/ 内,已被 .gitignore 排除。

例如启动密码模式 sshd:

bash scripts/ssh-server/password.sh # 在另一个终端:ssh admin@127.0.0.1 -p 1022 # 密码 123456

#4.3 构建与运行

仓库根没有统一 run.sh,每个 cmd/* 子包自带 run-*.sh 脚本(除 output/ 仅作演示外),它们做:
  1. source .env 注入 MSSH_HOST / MSSH_PORT / MSSH_USERNAME / MSSH_PASSWORD 等变量
  2. export MOONBIT_CLI_ARGS="..."(CLI 参数通过环境变量传入 main,避免对 argv 解析的兼容问题)
  3. moon clean && moon run . --target native(一次完成编译 + 运行)

CLI 参数在 main 里通过 @utils.get_argv()MOONBIT_CLI_ARGS 读取并 tokenize,因此所有命令行参数都集中在一个环境变量里。

#4.3.1 密码认证

# 前置:bash scripts/ssh-server/password.sh cd cmd/password ./run.sh # 内部: # source .env # 注入 MSSH_HOST=127.0.0.1 MSSH_PORT=1022 MSSH_USERNAME=admin MSSH_PASSWORD=123456 # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT --exec 'uname -a' --password $MSSH_PASSWORD" # moon clean && moon run . --target native

等价的手动调用:

export MOONBIT_CLI_ARGS="admin@127.0.0.1 --port 1022 --exec 'uname -a' --password 123456" cd cmd/password moon run . --target native

#4.3.2 公钥认证(Ed25519 / RSA / ECDSA)

cmd/key/ 提供三个独立脚本,对应三种密钥类型:

# 前置:bash scripts/ssh-server/key-ed25519.sh # 或 key-rsa.sh / key-ecdsa.sh cd cmd/key ./run-ed25519.sh # 默认 --key ${workspace}/scripts/ssh-server/id_ed25519 ./run-rsa.sh # 默认 --key ${workspace}/scripts/ssh-server/id_rsa ./run-ecdsa.sh # 默认 --key ${workspace}/scripts/ssh-server/id_ecdsa # 内部: # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT --exec 'uname -a' --key ${workspace}/scripts/ssh-server/id_<alg>" # moon clean && moon run . --target native

cmd/key/.env 注入 workspace(docker 容器内的工作目录)。如果 sshd 直接跑在本地,请把 ${workspace}/scripts/ssh-server/id_* 改成宿主机路径。

#4.3.3 SFTP 文件传输

cmd/sftp/run.sh 是一个端到端冒烟脚本:依次执行 ls / → mkdir → ls → put → ls → get → stat → rm → rmdir → ls。脚本先 moon build 一次,再多次切换 MOONBIT_CLI_ARGS 调用同一个二进制:

# 前置:bash scripts/ssh-server/password.sh cd cmd/sftp ./run.sh # 内部: # moon clean && moon build . --target native # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT --password $MSSH_PASSWORD --command ls /" # ../../_build/native/debug/build/cmd/sftp/sftp.exe # …(依次 mkdir / ls / put / ls / get / stat / rm / rmdir / ls)

支持的子命令:ls <path> / get <remote> / put <local> <remote> / rm <path> / mkdir <path> / rmdir <path> / stat <path>

注意: 当前 cmd_get 把内容以 hex 预览打印到 stdout,并未真正写入本地文件;cmd_put 上传的是一个固定的演示字符串(Hello from MoonSSH SFTP client!)。

#4.3.4 端口转发

端口转发通过 cmd/forwarding/ 入口;每种模式对应一个独立 run-*.sh 脚本:

# 前置:bash scripts/ssh-server/forwarding.sh # (启动 sshd 5022→2222,并把 1080 端口绑定到 nginx 作为统一目标; # 同时配置 AllowTcpForwarding=yes / GatewayPorts=yes / PermitOpen=any) cd cmd/forwarding # 1. 远程转发 (-R):远端 8080 → SSH 隧道 → 本地 localhost:1080 (nginx) ./run-remote.sh # 内部默认: # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT -R 8080:localhost:1080 --password $MSSH_PASSWORD" # moon clean && moon run . --target native # 验证(在 sshd 容器内): # docker exec openssh-server_forwarding curl http://127.0.0.1:8080 # 2. 本地转发 (-L):本地 2080 → SSH 隧道 → 远端 gateway:1080 (宿主机 nginx) ./run-local.sh # 内部默认: # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT -L 2080:$GATEWAY_IP:1080 --password $MSSH_PASSWORD" # 验证(在宿主机): # curl http://127.0.0.1:2080 # 3. SOCKS5 动态代理 (-D):本地 SOCKS5:3080 → SSH 隧道 → 任意远端目标 ./run-socks5.sh # 内部默认: # export MOONBIT_CLI_ARGS="$MSSH_USERNAME@$MSSH_HOST --port $MSSH_PORT -D 3080 --password $MSSH_PASSWORD" # 验证(在宿主机,通过 SOCKS5 访问远端 nginx): # curl --socks5 127.0.0.1:3080 http://$GATEWAY_IP:1080

实现要点:
  • 三种模式都共用一个 relay_data() 双向中继实现(select-style 轮询,避免阻塞任一方向)。
  • SOCKS5 当前仅支持 IPv4(ATYP=0x01)与域名(ATYP=0x03),IPv6(ATYP=0x04)直接返回 0x08 并关闭。
  • relay_data 内部把 channel 的 data_sink 切到 Socket(local_socket) 模式,绕开 Buffer 缓冲。
  • 集成测试在 .github/workflows/integration.yml 中跑全:依次 run-remote.shrun-local.shrun-socks5.sh

#5. 核心模块

#5.1 ConnectOptions

pub struct ConnectOptions {
host : String
port : Int
user : String
client_banner : String? // 默认 "SSH-2.0-MoonSSH_0.1.0"
host_key_policy : HostKeyPolicy // 默认 Strict, 无条目 -> 拒绝所有未知主机
timeout_ms : Int // 默认 30_000
}

安全默认值ConnectOptions::new(...) 默认策略是 Strict(entries=[], on_unknown=reject)——未显式配置则拒绝所有未知主机 请按下列四种模式之一显式启用校验:

方法适用场景
.with_strict_host_key_check(content)加载 OpenSSH 格式 known_hosts;命中放行,未命中拒绝;key 不一致抛 HostKeyMismatch(MITM 警告)。
.with_trust_on_first_use(content, prompt)首次连接由 prompt(host, alg, key) -> Bool 决定是否接受并持久化到 entries;之后按 Strict 校验。
.with_host_key_verifier(f)(alg, key) -> Bool 回调完全接管校验;适用于自定义指纹格式、HSM / 外部 Trust Store 集成。
.with_insecure_host_key()跳过 host key 校验。仅用于受信任内网 / 本地回环。公网禁用

content 由调用方读取 known_hosts 文件得到(本库不直接依赖文件系统,保持协议层纯净)。

构造示例:

// 1. 严格模式:加载本地 known_hosts
let kh = read_known_hosts("~/.ssh/known_hosts") // 调用方自己读
let opts = @src.ConnectOptions::new("example.com", 22, "alice")
.with_strict_host_key_check(kh)

// 2. TOFU:首次询问用户
let opts2 = @src.ConnectOptions::new("new.host.com", 22, "alice")
.with_trust_on_first_use("", (host, alg, key) => {
let fp = fingerprint(alg, key)
confirm("Trust \{host} (fp=\{fp})? [y/N]")
})

// 3. 自定义校验(HSM / 外部 Trust Store)
let opts3 = @src.ConnectOptions::new("example.com", 22, "alice")
.with_host_key_verifier((alg, key) => hsm.verify("ssh-host", alg, key))

// 4. 内网测试:跳过校验
let opts4 = @src.ConnectOptions::new("127.0.0.1", 2222, "test")
.with_insecure_host_key()

KEX 阶段在收到服务端公钥时根据策略产出 HostKeyDecision Accept → 继续;RejectMismatch / RejectUnknown / RejectByUser → 抛出 HostKeyMismatch,原因见 HostKeyDecision::reason(host, alg)

#5.2 Client 顶层 API

方法说明
Client::connect(opts) -> Client raise SshErrorTCP 连接 + banner 交换
Client::kex() -> Unit raise SshError完整 KEX(KEXINIT → DH/ECDH → NEWKEYS → 安装加密)
Client::auth_password(pwd) -> Unit raise SshError密码认证
Client::auth_publickey(key_path) -> Unit raise SshError公钥认证(two-step),自动适配 ssh-rsa / rsa-sha2-256 / rsa-sha2-512 / ssh-ed25519 / ecdsa-sha2-nistp256/384/521 / ssh-dss
Client::auth_keyboard_interactive(answer_fn) -> Unit raise SshErrorkeyboard-interactive 认证
Client::auth_auto(pwd, key_path?) -> Unit raise SshErrornone → publickey → kbd-int → password 自动回退
Client::open_session() -> Channel创建本地 channel(id 自增)
Client::exec(ch, cmd) -> (String, String) raise SshError执行命令,返回 (stdout, stderr)
Client::shell(ch) -> Unit raise SshError打开 shell 通道(不管理交互式 I/O)
Client::forward_local_port(local_port, remote_host, remote_port) -> Unit raise SshError本地端口转发(-L),阻塞运行
Client::forward_remote_port(remote_port, local_host, local_port) -> Unit raise SshError远程端口转发(-R),阻塞运行
Client::forward_socks5(local_port) -> Unit raise SshErrorSOCKS5 动态代理(-D),阻塞运行;支持 IPv4 + 域名,不支持 IPv6
Client::cancel_remote_forward(address, port) -> Unit raise SshError取消远程端口转发
Client::close() -> Unit关闭 TCP 连接

#5.3 Channel

pub struct Channel {
id : Int // 本地 channel id
peer_id : Int // 服务端分配的 channel id
state : ChannelState // Closed / Opening / Open / ExecPending / EofReceived / Closing / Done
channel_type : ChannelType // Session / DirectTcpip(host, port) / ForwardedTcpip(host, port)
data_sink : DataSink // Buffer / Socket(Tcp)
// ...
}

pub fn Channel::id(self) -> Int
pub fn Channel::peer_id(self) -> Int
pub fn Channel::state(self) -> ChannelState
pub fn Channel::is_open(self) -> Bool
pub fn Channel::exit_status(self) -> Int?

通道底层消息:OPEN / OPEN_CONFIRMATION / OPEN_FAILURE / WINDOW_ADJUST / DATA / EXTENDED_DATA / EOF / CLOSE / REQUEST(含 exit-status / exit-signal / exec / shell / pty-req)。

通道类型:
  • Session — exec / shell / subsystem
  • DirectTcpip(host, port) — 本地转发(-L),客户端发起
  • ForwardedTcpip(host, port) — 远程转发(-R),服务端发起

#5.4 SftpClient

SFTP v3 文件传输协议客户端:

pub struct SftpClient {
// 内部字段
priv client : Client
priv channel : Channel
priv version : Int // SFTP 协议版本,当前固定为 3
priv mut request_id : Int
}

pub struct SftpAttrs {
priv flags : Int
size : Int?
uid : Int?
gid : Int?
permissions : Int?
atime : Int?
mtime : Int?
}

pub struct SftpDirEntry {
filename : String
longname : String
attrs : SftpAttrs
}

// 初始化
pub fn SftpClient::open(client : Client) -> SftpClient raise SshError

// 高层 API
pub fn SftpClient::read_file(path : String) -> Bytes raise SshError
pub fn SftpClient::write_file(path : String, data : Bytes, permissions : Int) -> Unit raise SshError
pub fn SftpClient::listdir(path : String) -> Array[SftpDirEntry] raise SshError

// 底层 API
pub fn SftpClient::readdir(path : String) -> Array[SftpDirEntry] raise SshError
pub fn SftpClient::read(handle : Bytes, offset : Int, length : Int) -> Bytes raise SshError
pub fn SftpClient::write(handle : Bytes, offset : Int, data : Bytes) -> Unit raise SshError
pub fn SftpClient::close_handle(handle : Bytes) -> Unit raise SshError
pub fn SftpClient::stat(path : String) -> SftpAttrs raise SshError
pub fn SftpClient::remove(path : String) -> Unit raise SshError
pub fn SftpClient::mkdir(path : String, permissions : Int) -> Unit raise SshError
pub fn SftpClient::rmdir(path : String) -> Unit raise SshError
pub fn SftpClient::rename(oldpath : String, newpath : String) -> Unit raise SshError
pub fn SftpClient::realpath(path : String) -> String raise SshError
pub fn SftpClient::close() -> Unit

当前限制: 文件大小按 32 位 Int 读取,超过 4 GiB 的文件会被截断;SftpAttrs 仅解析 SSH_FILEXFER_ATTR_SIZE / UIDGID / PERMISSIONS / ACCESSTIME / MODIFYTIME,未识别的 flags 字段被忽略。

#5.5 known_hosts

src/known_hosts.mbt 提供 OpenSSH known_hosts 文件的解析与匹配:

pub struct KnownHost {
patterns : Array[String]
key_alg : String
key : Bytes
}

pub fn KnownHost::matches(self, host : String, port : Int) -> Bool
pub fn KnownHost::alg(self) -> String
pub fn KnownHost::key_bytes(self) -> Bytes

支持通配符(* / ?);host:port 形式自动展开为 [host]:port(当 port ≠ 22)。 尚未支持 HMAC-SHA1 哈希形式(|1|…)——遇到会直接返回不匹配。

#6. 开发流程

#6.1 修改后必跑

moon fmt # 格式化 moon info # 更新 .mbti 接口 moon build --target native # 编译 moon test --target native # 测试

#6.2 覆盖率

moon coverage analyze > uncovered.log # 目标:uncovered.log 中 packet / kex / auth 关键路径为空

#6.3 派生代码

src/socket/socket.mbt / socket.c)派生于 moonbitlang/async/tls(Apache 2.0)。任何修改请保留:

  • 原 Apache 2.0 版权头
  • 命名空间调整记录(moonbitlang/async/socketsrc/socket

#7. 路线图

版本内容状态
v0.1协议骨架:packet / kex 状态机 / auth(密码 + 公钥 + kbd-int + auto)/ channel / crypto FFI / 自带 socket FFI✅ 已发布
v0.2SFTP 协议✅ 已发布
v0.3端口转发(remote)✅ 已发布
v0.4端口转发(local / SOCKS5)✅ 已发布
v0.5shell 交互式 I/O / pty-req 对接📋 待开发
v0.6X11 转发(x11-req + x11 通道,RFC 4254 §6.3.2)📋 待开发

进展:

#8. 跨平台注意

平台编译运行说明
Linux glibc完全支持
Windows MinGW (Winsock2)完全支持
macOS完全支持

#9. 引用

#10. License

Apache-2.0。详见 LICENSE