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 }