- 拆分原有单体 ssh.go,按职责重组为 types、utils、transport、login、keepalive、session、exec、pool、shell、terminal、forward、hostkey、state 等模块,并补充平台相关实现 - 重做登录与连接运行时,补齐基于 context 的建连、jump/proxy 链路、可配置认证顺序,以及 Unix/Windows 下的 ssh-agent 支持 - 新增正式非交互执行模型 ExecRequest/ExecResult,支持流式输出、溢出统计、超时控制,以及 posix/powershell/cmd/raw 多方言执行 - 保留旧 shell 风格兼容接口,同时让路径/用户探测等 helper 具备跨 shell fallback,避免 Windows 目标继续硬依赖 POSIX 命令 - 新增 TerminalSession 作为原始交互终端基座,提供 IO attach、resize、signal/control、退出状态与关闭原因管理 - 重构端口转发语义,默认复用当前 SSH 连接,并显式提供 detached 的本地/动态转发模式承载隔离场景 - 梳理 keepalive 与取消语义,区分仅取消本次操作和关闭整条连接,并统一连接状态与传输关闭路径 - 围绕新的 session/连接生命周期重做执行池与运行时支撑 - 大幅增强 SFTP 传输链路,补齐更安全的原子替换、校验、进度回调、重试隔离、可复用 client 生命周期与失败语义 - 新增取消语义、keepalive、SFTP、forward、terminal input 等关键回归测试,提升核心链路稳定性
197 lines
5.1 KiB
Go
197 lines
5.1 KiB
Go
package starssh
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"io"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/pkg/sftp"
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
const (
|
|
defaultSSHPort = 22
|
|
defaultLoginTimeout = 5 * time.Second
|
|
defaultKeepAliveTimeout = 3 * time.Second
|
|
defaultShellPollInterval = 120 * time.Millisecond
|
|
defaultShellSetupDelay = 200 * time.Millisecond
|
|
defaultShellSetupTimeout = 3 * time.Second
|
|
defaultShellWaitTimeout = 30 * time.Second
|
|
defaultShellPromptToken = "__STARSSH_PROMPT__>"
|
|
defaultPTYTerm = "xterm"
|
|
defaultPTYRows = 500
|
|
defaultPTYColumns = 250
|
|
|
|
defaultTransferBufferSize = 1024 * 1024
|
|
|
|
defaultExecStreamMaxPendingChunks = 256
|
|
defaultExecStreamMaxPendingBytes = 4 * 1024 * 1024
|
|
)
|
|
|
|
type DialContextFunc func(ctx context.Context, network, address string) (net.Conn, error)
|
|
|
|
type ProxyType string
|
|
|
|
const (
|
|
ProxyTypeSOCKS5 ProxyType = "socks5"
|
|
ProxyTypeHTTPConnect ProxyType = "http_connect"
|
|
)
|
|
|
|
type ProxyConfig struct {
|
|
Type ProxyType
|
|
Addr string
|
|
Username string
|
|
Password string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type AuthMethodKind string
|
|
|
|
const (
|
|
AuthMethodPrivateKey AuthMethodKind = "private_key"
|
|
AuthMethodPassword AuthMethodKind = "password"
|
|
AuthMethodKeyboardInteractive AuthMethodKind = "keyboard_interactive"
|
|
AuthMethodSSHAgent AuthMethodKind = "ssh_agent"
|
|
)
|
|
|
|
type StarSSH struct {
|
|
stateMu sync.RWMutex
|
|
Client *ssh.Client
|
|
PublicKey ssh.PublicKey
|
|
PubkeyBase64 string
|
|
Hostname string
|
|
RemoteAddr net.Addr
|
|
Banner string
|
|
LoginInfo LoginInput
|
|
online bool
|
|
upstream *StarSSH
|
|
sftpClient *sftp.Client
|
|
sftpMu sync.Mutex
|
|
keepaliveMu sync.Mutex
|
|
keepaliveStop chan struct{}
|
|
keepaliveDone chan struct{}
|
|
}
|
|
|
|
type LoginInput struct {
|
|
KeyExchanges []string
|
|
Ciphers []string
|
|
MACs []string
|
|
User string
|
|
Password string
|
|
PasswordCallback func() (string, error)
|
|
KeyboardInteractiveCallback ssh.KeyboardInteractiveChallenge
|
|
Prikey string
|
|
PrikeyPwd string
|
|
DisableSSHAgent bool
|
|
AuthOrder []AuthMethodKind
|
|
Addr string
|
|
Port int
|
|
Timeout time.Duration
|
|
DialContext DialContextFunc
|
|
Proxy *ProxyConfig
|
|
Jump *LoginInput
|
|
KeepAliveInterval time.Duration
|
|
KeepAliveTimeout time.Duration
|
|
HostKeyCallback func(string, net.Addr, ssh.PublicKey) error
|
|
BannerCallback func(string) error
|
|
}
|
|
|
|
// StarShell keeps the legacy prompt-driven helper for POSIX-style scripted shell interactions.
|
|
// It is not a generic cross-shell abstraction; for product-grade interactive terminals, prefer TerminalSession.
|
|
type StarShell struct {
|
|
Keyword string
|
|
UseWaitDefault bool
|
|
WaitTimeout time.Duration
|
|
Session *ssh.Session
|
|
in io.Writer
|
|
out *bufio.Reader
|
|
er *bufio.Reader
|
|
outbyte []byte
|
|
errbyte []byte
|
|
lastout int64
|
|
errors error
|
|
isprint bool
|
|
isfuncs bool
|
|
iscolor bool
|
|
isecho bool
|
|
rw sync.RWMutex
|
|
funcs func(string)
|
|
writeMu sync.Mutex
|
|
commandMu sync.Mutex
|
|
closeOnce sync.Once
|
|
promptToken string
|
|
}
|
|
|
|
type TerminalConfig struct {
|
|
Term string
|
|
Rows int
|
|
Columns int
|
|
Modes ssh.TerminalModes
|
|
}
|
|
|
|
type TerminalControl byte
|
|
|
|
const (
|
|
TerminalControlInterrupt TerminalControl = 0x03
|
|
TerminalControlEOF TerminalControl = 0x04
|
|
TerminalControlBell TerminalControl = 0x07
|
|
TerminalControlBackspace TerminalControl = 0x08
|
|
TerminalControlLineKill TerminalControl = 0x15
|
|
TerminalControlQuit TerminalControl = 0x1c
|
|
TerminalControlSuspend TerminalControl = 0x1a
|
|
TerminalControlPauseOutput TerminalControl = 0x13
|
|
TerminalControlResumeOutput TerminalControl = 0x11
|
|
)
|
|
|
|
type TerminalCloseReason string
|
|
|
|
const (
|
|
TerminalCloseReasonUnknown TerminalCloseReason = ""
|
|
TerminalCloseReasonExit TerminalCloseReason = "exit"
|
|
TerminalCloseReasonSignal TerminalCloseReason = "signal"
|
|
TerminalCloseReasonClosed TerminalCloseReason = "closed"
|
|
TerminalCloseReasonContextCanceled TerminalCloseReason = "context_canceled"
|
|
TerminalCloseReasonDeadlineExceeded TerminalCloseReason = "deadline_exceeded"
|
|
TerminalCloseReasonTransportError TerminalCloseReason = "transport_error"
|
|
)
|
|
|
|
type TerminalExitInfo struct {
|
|
ExitCode int
|
|
ExitSignal string
|
|
ExitMessage string
|
|
Reason TerminalCloseReason
|
|
}
|
|
|
|
type TerminalSession struct {
|
|
Session *ssh.Session
|
|
ID string
|
|
Label string
|
|
Metadata map[string]string
|
|
stdin io.WriteCloser
|
|
stdout io.Reader
|
|
stderr io.Reader
|
|
|
|
attachMu sync.RWMutex
|
|
in io.Reader
|
|
out io.Writer
|
|
errOut io.Writer
|
|
|
|
runOnce sync.Once
|
|
runDone chan struct{}
|
|
runErr error
|
|
|
|
waitOnce sync.Once
|
|
waitDone chan struct{}
|
|
|
|
stateMu sync.RWMutex
|
|
waitErr error
|
|
exitInfo TerminalExitInfo
|
|
closeReason TerminalCloseReason
|
|
closeErr error
|
|
|
|
closeOnce sync.Once
|
|
}
|