135 lines
3.0 KiB
Go
Executable File
135 lines
3.0 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) != 3 {
|
|
fmt.Println("Usage: go run ssh_client.go <user> <hostname:port>")
|
|
os.Exit(1)
|
|
}
|
|
|
|
user := os.Args[1]
|
|
address := os.Args[2]
|
|
|
|
fmt.Print("Enter Password: ")
|
|
password, err := term.ReadPassword(int(os.Stdin.Fd()))
|
|
if err != nil {
|
|
log.Fatalf("Failed to read password: %v", err)
|
|
}
|
|
fmt.Println()
|
|
|
|
// Establish TCP connection
|
|
netConn, err := net.Dial("tcp", address)
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to %s: %v", address, err)
|
|
}
|
|
|
|
// Use the connection to create an SSH client
|
|
err = runSSHClient(user, string(password), netConn)
|
|
if err != nil {
|
|
log.Fatalf("SSH session failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func runSSHClient(user, password string, netConn net.Conn) error {
|
|
config := &ssh.ClientConfig{
|
|
User: user,
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.Password(password),
|
|
},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
}
|
|
|
|
// Create SSH connection from net.Conn
|
|
conn, chans, reqs, err := ssh.NewClientConn(netConn, netConn.RemoteAddr().String(), config)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create SSH client connection: %v", err)
|
|
}
|
|
|
|
client := ssh.NewClient(conn, chans, reqs)
|
|
defer client.Close()
|
|
|
|
session, err := client.NewSession()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create session: %v", err)
|
|
}
|
|
defer session.Close()
|
|
|
|
modes := ssh.TerminalModes{
|
|
ssh.ECHO: 1,
|
|
ssh.TTY_OP_ISPEED: 14400,
|
|
ssh.TTY_OP_OSPEED: 14400,
|
|
}
|
|
|
|
fd := int(os.Stdin.Fd())
|
|
oldState, err := term.MakeRaw(fd)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set raw mode: %v", err)
|
|
}
|
|
defer term.Restore(fd, oldState)
|
|
|
|
width, height, err := term.GetSize(fd)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get terminal size: %v", err)
|
|
}
|
|
|
|
if err := session.RequestPty("xterm", height, width, modes); err != nil {
|
|
return fmt.Errorf("request for pseudo terminal failed: %v", err)
|
|
}
|
|
|
|
session.Stdout = os.Stdout
|
|
session.Stderr = os.Stderr
|
|
session.Stdin = os.Stdin
|
|
|
|
if err := session.Shell(); err != nil {
|
|
return fmt.Errorf("failed to start shell: %v", err)
|
|
}
|
|
|
|
// Handle window size changes
|
|
go handleWindowChange(session, fd)
|
|
|
|
// Handle Ctrl+C (SIGINT)
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT)
|
|
go func() {
|
|
for range sigChan {
|
|
session.Signal(ssh.SIGINT)
|
|
}
|
|
}()
|
|
|
|
if err := session.Wait(); err != nil {
|
|
if e, ok := err.(*ssh.ExitError); ok {
|
|
os.Exit(e.ExitStatus())
|
|
}
|
|
return fmt.Errorf("failed to wait for session: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleWindowChange(session *ssh.Session, fd int) {
|
|
sigwinchCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigwinchCh, syscall.SIGWINCH)
|
|
|
|
for range sigwinchCh {
|
|
width, height, err := term.GetSize(fd)
|
|
if err != nil {
|
|
log.Printf("Failed to get window size: %v", err)
|
|
continue
|
|
}
|
|
if err := session.WindowChange(height, width); err != nil {
|
|
log.Printf("Failed to send window change request: %v", err)
|
|
}
|
|
}
|
|
}
|