1
1
package chclient
2
2
3
3
import (
4
- "errors"
5
4
"fmt"
5
+ "io"
6
6
"net"
7
7
"net/url"
8
8
"regexp"
9
9
"strings"
10
10
"time"
11
11
12
- "github.com/hashicorp/yamux"
13
12
"github.com/jpillora/backoff"
14
13
"github.com/jpillora/chisel/share"
15
- "github.com/jpillora/conncrypt "
14
+ "golang.org/x/crypto/ssh "
16
15
"golang.org/x/net/websocket"
17
16
)
18
17
19
18
type Client struct {
20
19
* chshare.Logger
21
20
config * chshare.Config
22
- encconfig []byte
23
- key , server string
21
+ sshConfig * ssh.ClientConfig
24
22
proxies []* Proxy
25
- session * yamux.Session
23
+ sshConn ssh.Conn
24
+ fingerprint string
25
+ server string
26
+ keyPrefix string
26
27
running bool
27
28
runningc chan error
28
29
}
29
30
30
- func NewClient (key , server string , remotes ... string ) (* Client , error ) {
31
+ func NewClient (keyPrefix , auth , server string , remotes ... string ) (* Client , error ) {
31
32
32
33
//apply default scheme
33
34
if ! strings .HasPrefix (server , "http" ) {
@@ -60,20 +61,27 @@ func NewClient(key, server string, remotes ...string) (*Client, error) {
60
61
config .Remotes = append (config .Remotes , r )
61
62
}
62
63
63
- encconfig , err := chshare .EncodeConfig (config )
64
- if err != nil {
65
- return nil , fmt .Errorf ("Failed to encode config: %s" , err )
66
- }
67
-
68
- return & Client {
64
+ c := & Client {
69
65
Logger : chshare .NewLogger ("client" ),
70
66
config : config ,
71
- encconfig : encconfig ,
72
- key : key ,
73
67
server : u .String (),
68
+ keyPrefix : keyPrefix ,
74
69
running : true ,
75
70
runningc : make (chan error , 1 ),
76
- }, nil
71
+ }
72
+
73
+ c .sshConfig = & ssh.ClientConfig {
74
+ ClientVersion : chshare .ProtocolVersion + "-client" ,
75
+ HostKeyCallback : c .verifyServer ,
76
+ }
77
+
78
+ user , pass := chshare .ParseAuth (auth )
79
+ if user != "" {
80
+ c .sshConfig .User = user
81
+ c .sshConfig .Auth = []ssh.AuthMethod {ssh .Password (pass )}
82
+ }
83
+
84
+ return c , nil
77
85
}
78
86
79
87
//Start then Wait
@@ -82,6 +90,15 @@ func (c *Client) Run() error {
82
90
return c .Wait ()
83
91
}
84
92
93
+ func (c * Client ) verifyServer (hostname string , remote net.Addr , key ssh.PublicKey ) error {
94
+ f := chshare .FingerprintKey (key )
95
+ if c .keyPrefix != "" && ! strings .HasPrefix (f , c .keyPrefix ) {
96
+ return fmt .Errorf ("Invalid fingerprint (Got %s)" , f )
97
+ }
98
+ c .fingerprint = f
99
+ return nil
100
+ }
101
+
85
102
//Starts the client
86
103
func (c * Client ) Start () {
87
104
go c .start ()
@@ -90,37 +107,24 @@ func (c *Client) Start() {
90
107
func (c * Client ) start () {
91
108
c .Infof ("Connecting to %s\n " , c .server )
92
109
93
- //proxies all use this function
94
- openStream := func () (net.Conn , error ) {
95
- if c .session == nil || c .session .IsClosed () {
96
- return nil , c .Errorf ("no session available" )
97
- }
98
- stream , err := c .session .Open ()
99
- if err != nil {
100
- return nil , err
101
- }
102
- return stream , nil
103
- }
104
-
105
110
//prepare proxies
106
111
for id , r := range c .config .Remotes {
107
- proxy := NewProxy (c , id , r , openStream )
112
+ proxy := NewProxy (c , id , r )
108
113
go proxy .start ()
109
114
c .proxies = append (c .proxies , proxy )
110
115
}
111
116
117
+ //connection loop!
112
118
var connerr error
113
- b := & backoff.Backoff {Max : 15 * time .Second }
119
+ b := & backoff.Backoff {Max : 5 * time .Minute }
114
120
115
- //connection loop!
116
121
for {
117
122
if ! c .running {
118
123
break
119
124
}
120
125
if connerr != nil {
121
126
d := b .Duration ()
122
- c .Infof ("Connerr: %v" , connerr )
123
- c .Infof ("Retrying in %s..." , d )
127
+ c .Infof ("Retrying in %s...\n " , d )
124
128
connerr = nil
125
129
time .Sleep (d )
126
130
}
@@ -131,58 +135,43 @@ func (c *Client) start() {
131
135
continue
132
136
}
133
137
134
- conn := net .Conn (ws )
135
-
136
- if c .key != "" {
137
- conn = conncrypt .New (conn , & conncrypt.Config {Password : c .key })
138
+ sshConn , chans , reqs , err := ssh .NewClientConn (ws , "" , c .sshConfig )
139
+ //NOTE break -> dont retry on handshake failures
140
+ if err != nil {
141
+ if strings .Contains (err .Error (), "unable to authenticate" ) {
142
+ c .Infof ("Authentication failed" )
143
+ } else {
144
+ c .Infof (err .Error ())
145
+ }
146
+ break
138
147
}
139
-
140
- //write config, read result
141
- chshare .SizeWrite (conn , c .encconfig )
142
-
143
- resp := chshare .SizeRead (conn )
144
- if string (resp ) != "Handshake Success" {
145
- //no point in retrying
146
- c .runningc <- errors .New ("Handshake failed" )
147
- conn .Close ()
148
+ conf , _ := chshare .EncodeConfig (c .config )
149
+ _ , conerr , err := sshConn .SendRequest ("config" , true , conf )
150
+ if err != nil {
151
+ c .Infof ("Config verification failed" , c .fingerprint )
152
+ break
153
+ }
154
+ if len (conerr ) > 0 {
155
+ c .Infof (string (conerr ))
148
156
break
149
157
}
150
158
151
- // Setup client side of yamux
152
- c .session , err = yamux .Client (conn , nil )
153
- if err != nil {
159
+ c .Infof ("Connected (%s)" , c .fingerprint )
160
+ //connected
161
+ b .Reset ()
162
+
163
+ c .sshConn = sshConn
164
+ go ssh .DiscardRequests (reqs )
165
+ go chshare .RejectStreams (chans )
166
+ err = sshConn .Wait ()
167
+ //disconnected
168
+ c .sshConn = nil
169
+ if err != nil && err != io .EOF {
154
170
connerr = err
171
+ c .Infof ("Disconnection error: %s" , err )
155
172
continue
156
173
}
157
- b .Reset ()
158
-
159
- //check latency
160
- go func () {
161
- d , err := c .session .Ping ()
162
- if err == nil {
163
- c .Infof ("Connected (Latency: %s)\n " , d )
164
- } else {
165
- c .Infof ("Connected\n " )
166
- }
167
- }()
168
-
169
- //signal is connected
170
- connected := make (chan bool )
171
-
172
- //poll websocket state
173
- go func () {
174
- for {
175
- if c .session .IsClosed () {
176
- connerr = c .Errorf ("disconnected" )
177
- c .Infof ("Disconnected\n " )
178
- close (connected )
179
- break
180
- }
181
- time .Sleep (100 * time .Millisecond )
182
- }
183
- }()
184
- //block!
185
- <- connected
174
+ c .Infof ("Disconnected\n " )
186
175
}
187
176
close (c .runningc )
188
177
}
@@ -195,8 +184,8 @@ func (c *Client) Wait() error {
195
184
//Close manual stops the client
196
185
func (c * Client ) Close () error {
197
186
c .running = false
198
- if c .session == nil {
187
+ if c .sshConn == nil {
199
188
return nil
200
189
}
201
- return c .session .Close ()
190
+ return c .sshConn .Close ()
202
191
}
0 commit comments