starssh/types.go
starainrt 1625997d8f
fix: 拆分 starssh 的拨号超时与认证超时语义
- 为 LoginInput 新增 DialTimeout,明确区分【TCP/proxy/ssh-agent 拨号超时】和【SSH 握手/认证超时】
- 将 Timeout 收口为握手/认证阶段超时,0 表示不限制,不再在登录入口自动回填默认值
- 新增 effectiveLoginTimeout/effectiveDialTimeout,统一超时决策逻辑
- 调整 login 流程,仅对 login context、ssh.ClientConfig 和握手阶段连接 deadline 使用认证超时
- 调整 transport 拨号链路,默认 TCP dial、proxy dial 与 ssh-agent 建连统一改用 DialTimeout
- 修正 agent forwarding 初始化仍错误复用 LoginInfo.Timeout 的问题
- 保持 LoginSimple 的直观行为:传入 timeout 时同时映射到 Timeout 和 DialTimeout
- 新增 login_timeout_test,覆盖零值不回填、DialTimeout 优先级,以及 ssh-agent 认证路径使用拨号超时的回归测试
2026-04-26 23:29:36 +08:00

208 lines
5.6 KiB
Go

package starssh
import (
"bufio"
"context"
"io"
"net"
"sync"
"sync/atomic"
"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
agentForwardMu sync.Mutex
agentForwarder io.Closer
keepaliveMu sync.Mutex
keepaliveStop chan struct{}
keepaliveDone chan struct{}
closing atomic.Bool
}
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
ForwardSSHAgent bool
AuthOrder []AuthMethodKind
Addr string
Port int
// Timeout limits the SSH handshake/authentication phase after a TCP connection has
// already been established. Zero means no authentication timeout.
Timeout time.Duration
// DialTimeout limits outbound dial steps such as TCP connect, proxy connect, and
// local ssh-agent socket connect. Zero falls back to Timeout when set, otherwise
// uses the package default dial timeout. Negative disables the default dial timeout.
DialTimeout 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
}