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 defaultSSHAgentTimeout = 2 * time.Minute 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 SSHAgentDebugFunc func(SSHAgentDebugEvent) type SSHAgentDebugEvent struct { Step string Source string Endpoint string Network string Phase string Status string Duration time.Duration KeyCount int Err error } 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 // IdentityAgent overrides the local ssh-agent endpoint used for authentication // and agent forwarding. Empty uses SSH_AUTH_SOCK, or the platform default where // one exists. IdentityAgent string 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 // SSHAgentTimeout limits ssh-agent protocol operations such as listing keys and // signing challenges. Zero uses the package default, and negative disables the // per-operation deadline. This is intentionally separate from Timeout and // DialTimeout because hardware-backed agents may require a PIN or touch confirmation. SSHAgentTimeout time.Duration // SSHAgentForwardTimeout limits idle reads and writes on forwarded agent // channels. Zero or negative leaves forwarded channels without an idle deadline. SSHAgentForwardTimeout time.Duration // SSHAgentDebug receives structured ssh-agent dial/protocol events. It is nil by // default and must not log private key material. SSHAgentDebug SSHAgentDebugFunc 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 }