@@ -3,9 +3,12 @@ package ssh
33import (
44 "fmt"
55 "net"
6+ "os"
7+ "strings"
68 "time"
79
810 "golang.org/x/crypto/ssh"
11+ "golang.org/x/net/html"
912)
1013
1114// Client represents an SSH client that can operate over any net.Conn
@@ -31,6 +34,26 @@ func NewOverWebSocket(conn net.Conn, username, password string) *OverWebSocket {
3134 }
3235}
3336
37+ // stripHTMLTags removes HTML tags from a string and returns plain text.
38+ func stripHTMLTags (htmlStr string ) string {
39+ doc , err := html .Parse (strings .NewReader (htmlStr ))
40+ if err != nil {
41+ return htmlStr // fallback to original if parsing fails
42+ }
43+ var b strings.Builder
44+ var f func (* html.Node )
45+ f = func (n * html.Node ) {
46+ if n .Type == html .TextNode {
47+ b .WriteString (n .Data )
48+ }
49+ for c := n .FirstChild ; c != nil ; c = c .NextSibling {
50+ f (c )
51+ }
52+ }
53+ f (doc )
54+ return b .String ()
55+ }
56+
3457// StartTransport initializes the SSH client over the WebSocket connection
3558func (s * OverWebSocket ) StartTransport () error {
3659 fmt .Println ("→ Starting SSH transport over WebSocket connection..." )
@@ -42,27 +65,38 @@ func (s *OverWebSocket) StartTransport() error {
4265 tcpConn .SetKeepAlivePeriod (30 * time .Second )
4366 }
4467
45- // Clear any previous deadlines
46- s . conn . SetReadDeadline ( time.Time {})
47- s .conn .SetWriteDeadline (time.Time {} )
68+ // Set a deadline for the SSH handshake to avoid hanging
69+ handshakeTimeout := 15 * time .Second
70+ s .conn .SetDeadline (time .Now (). Add ( handshakeTimeout ) )
4871
4972 config := & ssh.ClientConfig {
5073 User : s .username ,
5174 Auth : []ssh.AuthMethod {
5275 ssh .Password (s .password ),
5376 },
5477 HostKeyCallback : ssh .InsecureIgnoreHostKey (), // WARNING: This is insecure for production
55- Timeout : 15 * time .Second ,
78+ Timeout : handshakeTimeout ,
79+ BannerCallback : func (message string ) error {
80+ plain := stripHTMLTags (message )
81+ fmt .Fprintln (stderrOrStdout (), plain )
82+ return nil
83+ },
5684 }
5785
5886 fmt .Printf ("→ Attempting SSH connection with user: %s\n " , s .username )
5987
6088 // Create SSH client using the WebSocket connection
6189 sshConn , chans , reqs , err := ssh .NewClientConn (s .conn , "tcp" , config )
6290 if err != nil {
91+ if nErr , ok := err .(net.Error ); ok && nErr .Timeout () {
92+ return fmt .Errorf ("SSH handshake timed out after %v" , handshakeTimeout )
93+ }
6394 return fmt .Errorf ("failed to create SSH connection: %v" , err )
6495 }
6596
97+ // Clear deadline after handshake
98+ s .conn .SetDeadline (time.Time {})
99+
66100 s .sshClient = ssh .NewClient (sshConn , chans , reqs )
67101 fmt .Println ("✓ SSH transport established and authenticated." )
68102 return nil
@@ -80,3 +114,9 @@ func (s *OverWebSocket) Close() error {
80114 }
81115 return nil
82116}
117+
118+ // stderrOrStdout returns stderr if available, otherwise stdout.
119+ func stderrOrStdout () * os.File {
120+ // fallback to stdout if stderr is not available
121+ return os .Stderr
122+ }
0 commit comments