1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package srvconn
import (
"io"
"sync"
"time"
gossh "golang.org/x/crypto/ssh"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
)
type ServerSSHConnection struct {
User *model.User
Asset *model.Asset
SystemUser *model.SystemUser
Overtime time.Duration
CloseOnce *sync.Once
ReuseConnection bool
client *SSHClient
session *gossh.Session
stdin io.WriteCloser
stdout io.Reader
}
func (sc *ServerSSHConnection) SetSSHClient(client *SSHClient) {
if client != nil {
sc.client = client
}
}
func (sc *ServerSSHConnection) Protocol() string {
return "ssh"
}
func (sc *ServerSSHConnection) invokeShell(h, w int, term string) (err error) {
sess, err := sc.client.NewSession()
if err != nil {
return
}
sc.session = sess
modes := gossh.TerminalModes{
gossh.ECHO: 1, // enable echoing
gossh.TTY_OP_ISPEED: 14400, // input speed = 14.4 kbaud
gossh.TTY_OP_OSPEED: 14400, // output speed = 14.4 kbaud
}
err = sess.RequestPty(term, h, w, modes)
if err != nil {
return
}
sc.stdin, err = sess.StdinPipe()
if err != nil {
return
}
sc.stdout, err = sess.StdoutPipe()
if err != nil {
return
}
err = sess.Shell()
return err
}
func (sc *ServerSSHConnection) Connect(h, w int, term string) (err error) {
if sc.client == nil {
sc.client, err = NewClient(sc.User, sc.Asset, sc.SystemUser, sc.Timeout(), sc.ReuseConnection)
if err != nil {
logger.Errorf("New SSH client err: %s", err)
return
}
}
err = sc.invokeShell(h, w, term)
if err != nil {
logger.Errorf("SSH client %p start ssh shell session err %s", sc.client, err)
RecycleClient(sc.client)
return
}
logger.Infof("SSH client %p start ssh shell session success", sc.client)
return
}
func (sc *ServerSSHConnection) SetWinSize(h, w int) error {
return sc.session.WindowChange(h, w)
}
func (sc *ServerSSHConnection) Read(p []byte) (n int, err error) {
return sc.stdout.Read(p)
}
func (sc *ServerSSHConnection) Write(p []byte) (n int, err error) {
return sc.stdin.Write(p)
}
func (sc *ServerSSHConnection) Timeout() time.Duration {
if sc.Overtime == 0 {
sc.Overtime = 30 * time.Second
}
return sc.Overtime
}
func (sc *ServerSSHConnection) Close() (err error) {
sc.CloseOnce.Do(func() {
RecycleClient(sc.client)
})
return sc.session.Close()
}