diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a0b1c37 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {}, + "args": [] + } + ] +} \ No newline at end of file diff --git a/utils/ssh/main.go b/utils/ssh/main.go new file mode 100644 index 0000000..e19e4c6 --- /dev/null +++ b/utils/ssh/main.go @@ -0,0 +1,185 @@ +package main + +import ( + "flag" + "fmt" + "io" + "os" + "os/signal" + "syscall" + "time" + + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" +) + +type SSHTerminal struct { + Session *ssh.Session + exitMsg string + stdout io.Reader + stdin io.Writer + stderr io.Reader +} + +func main() { + pwd := flag.String("pwd", "", "password") + user := flag.String("user", "root", "user") + ip := flag.String("ip", "", "ip") + port := flag.Int("port", 22, "ssh port") + flag.Parse() + + if *ip == "" || *pwd == "" { + fmt.Println("ip and passwod must be not null") + os.Exit(3) + } + + sshConfig := &ssh.ClientConfig{ + User: *user, + Auth: []ssh.AuthMethod{ + ssh.Password(*pwd), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", *ip, *port), sshConfig) + if err != nil { + fmt.Println(err) + } + defer client.Close() + + err = New(client) + if err != nil { + fmt.Println(err) + } +} + +func (t *SSHTerminal) updateTerminalSize() { + go func() { + // SIGWINCH is sent to the process when the window size of the terminal has + // changed. + sigwinchCh := make(chan os.Signal, 1) + signal.Notify(sigwinchCh, syscall.SIGWINCH) + + fd := int(os.Stdin.Fd()) + termWidth, termHeight, err := terminal.GetSize(fd) + if err != nil { + fmt.Println(err) + } + + for { + select { + // The client updated the size of the local PTY. This change needs to occur + // on the server side PTY as well. + case sigwinch := <-sigwinchCh: + if sigwinch == nil { + return + } + currTermWidth, currTermHeight, err := terminal.GetSize(fd) + + // Terminal size has not changed, don't do anything. + if currTermHeight == termHeight && currTermWidth == termWidth { + continue + } + + t.Session.WindowChange(currTermHeight, currTermWidth) + if err != nil { + fmt.Printf("Unable to send window-change reqest: %s.", err) + continue + } + + termWidth, termHeight = currTermWidth, currTermHeight + + } + } + }() + +} + +func (t *SSHTerminal) interactiveSession() error { + defer func() { + if t.exitMsg == "" { + fmt.Fprintln(os.Stdout, "the connection was closed on the remote side on ", time.Now().Format(time.RFC822)) + } else { + fmt.Fprintln(os.Stdout, t.exitMsg) + } + }() + + fd := int(os.Stdin.Fd()) + state, err := terminal.MakeRaw(fd) + if err != nil { + return err + } + defer terminal.Restore(fd, state) + + termWidth, termHeight, err := terminal.GetSize(fd) + if err != nil { + return err + } + + termType := os.Getenv("TERM") + if termType == "" { + termType = "xterm-256color" + } + + err = t.Session.RequestPty(termType, termHeight, termWidth, ssh.TerminalModes{}) + if err != nil { + return err + } + + t.updateTerminalSize() + + t.stdin, err = t.Session.StdinPipe() + if err != nil { + return err + } + t.stdout, err = t.Session.StdoutPipe() + if err != nil { + return err + } + t.stderr, err = t.Session.StderrPipe() + + go io.Copy(os.Stderr, t.stderr) + go io.Copy(os.Stdout, t.stdout) + go func() { + buf := make([]byte, 128) + for { + n, err := os.Stdin.Read(buf) + if err != nil { + fmt.Println(err) + return + } + if n > 0 { + _, err = t.stdin.Write(buf[:n]) + if err != nil { + fmt.Println(err) + t.exitMsg = err.Error() + return + } + } + } + }() + + err = t.Session.Shell() + if err != nil { + return err + } + err = t.Session.Wait() + if err != nil { + return err + } + return nil +} + +func New(client *ssh.Client) error { + session, err := client.NewSession() + if err != nil { + return err + } + defer session.Close() + + s := SSHTerminal{ + Session: session, + } + + return s.interactiveSession() +}