@@ -11,10 +11,13 @@ import (
11
11
"strings"
12
12
"time"
13
13
14
+ "github.com/armon/go-socks5"
14
15
"github.com/gorilla/websocket"
15
16
"github.com/jpillora/backoff"
16
17
chshare "github.com/jpillora/chisel/share"
18
+
17
19
"golang.org/x/crypto/ssh"
20
+ "golang.org/x/net/proxy"
18
21
)
19
22
20
23
//Config represents a client configuration
@@ -26,22 +29,24 @@ type Config struct {
26
29
MaxRetryCount int
27
30
MaxRetryInterval time.Duration
28
31
Server string
29
- HTTPProxy string
32
+ Proxy string
30
33
Remotes []string
31
- HostHeader string
34
+ Headers http.Header
35
+ DialContext func (ctx context.Context , network , addr string ) (net.Conn , error )
32
36
}
33
37
34
38
//Client represents a client instance
35
39
type Client struct {
36
40
* chshare.Logger
37
- config * Config
38
- sshConfig * ssh.ClientConfig
39
- sshConn ssh.Conn
40
- httpProxyURL * url.URL
41
- server string
42
- running bool
43
- runningc chan error
44
- connStats chshare.ConnStats
41
+ config * Config
42
+ sshConfig * ssh.ClientConfig
43
+ sshConn ssh.Conn
44
+ proxyURL * url.URL
45
+ server string
46
+ running bool
47
+ runningc chan error
48
+ connStats chshare.ConnStats
49
+ socksServer * socks5.Server
45
50
}
46
51
47
52
//NewClient creates a new client instance
@@ -68,11 +73,15 @@ func NewClient(config *Config) (*Client, error) {
68
73
//swap to websockets scheme
69
74
u .Scheme = strings .Replace (u .Scheme , "http" , "ws" , 1 )
70
75
shared := & chshare.Config {}
76
+ createSocksServer := false
71
77
for _ , s := range config .Remotes {
72
78
r , err := chshare .DecodeRemote (s )
73
79
if err != nil {
74
80
return nil , fmt .Errorf ("Failed to decode remote '%s': %s" , s , err )
75
81
}
82
+ if r .Socks && r .Reverse {
83
+ createSocksServer = true
84
+ }
76
85
shared .Remotes = append (shared .Remotes , r )
77
86
}
78
87
config .shared = shared
@@ -85,8 +94,8 @@ func NewClient(config *Config) (*Client, error) {
85
94
}
86
95
client .Info = true
87
96
88
- if p := config .HTTPProxy ; p != "" {
89
- client .httpProxyURL , err = url .Parse (p )
97
+ if p := config .Proxy ; p != "" {
98
+ client .proxyURL , err = url .Parse (p )
90
99
if err != nil {
91
100
return nil , fmt .Errorf ("Invalid proxy URL (%s)" , err )
92
101
}
@@ -102,6 +111,14 @@ func NewClient(config *Config) (*Client, error) {
102
111
Timeout : 30 * time .Second ,
103
112
}
104
113
114
+ if createSocksServer {
115
+ socksConfig := & socks5.Config {}
116
+ client .socksServer , err = socks5 .New (socksConfig )
117
+ if err != nil {
118
+ return nil , err
119
+ }
120
+ }
121
+
105
122
return client , nil
106
123
}
107
124
@@ -129,8 +146,8 @@ func (c *Client) verifyServer(hostname string, remote net.Addr, key ssh.PublicKe
129
146
//Start client and does not block
130
147
func (c * Client ) Start (ctx context.Context ) error {
131
148
via := ""
132
- if c .httpProxyURL != nil {
133
- via = " via " + c .httpProxyURL .String ()
149
+ if c .proxyURL != nil {
150
+ via = " via " + c .proxyURL .String ()
134
151
}
135
152
//prepare non-reverse proxies
136
153
for i , r := range c .config .shared .Remotes {
@@ -192,20 +209,40 @@ func (c *Client) connectionLoop() {
192
209
WriteBufferSize : 1024 ,
193
210
HandshakeTimeout : 45 * time .Second ,
194
211
Subprotocols : []string {chshare .ProtocolVersion },
212
+ NetDialContext : c .config .DialContext ,
195
213
}
196
- //optionally CONNECT proxy
197
- if c .httpProxyURL != nil {
198
- d .Proxy = func (* http.Request ) (* url.URL , error ) {
199
- return c .httpProxyURL , nil
200
- }
201
- }
202
- wsHeaders := http.Header {}
203
- if c .config .HostHeader != "" {
204
- wsHeaders = http.Header {
205
- "Host" : {c .config .HostHeader },
214
+ //optionally proxy
215
+ if c .proxyURL != nil {
216
+ if strings .HasPrefix (c .proxyURL .Scheme , "socks" ) {
217
+ // SOCKS5 proxy
218
+ if c .proxyURL .Scheme != "socks" && c .proxyURL .Scheme != "socks5h" {
219
+ c .Infof (
220
+ "unsupported socks proxy type: %s:// (only socks5h:// or socks:// is supported)" ,
221
+ c .proxyURL .Scheme )
222
+ break
223
+ }
224
+ var auth * proxy.Auth = nil
225
+ if c .proxyURL .User != nil {
226
+ pass , _ := c .proxyURL .User .Password ()
227
+ auth = & proxy.Auth {
228
+ User : c .proxyURL .User .Username (),
229
+ Password : pass ,
230
+ }
231
+ }
232
+ socksDialer , err := proxy .SOCKS5 ("tcp" , c .proxyURL .Host , auth , proxy .Direct )
233
+ if err != nil {
234
+ connerr = err
235
+ continue
236
+ }
237
+ d .NetDial = socksDialer .Dial
238
+ } else {
239
+ // CONNECT proxy
240
+ d .Proxy = func (* http.Request ) (* url.URL , error ) {
241
+ return c .proxyURL , nil
242
+ }
206
243
}
207
244
}
208
- wsConn , _ , err := d .Dial (c .server , wsHeaders )
245
+ wsConn , _ , err := d .Dial (c .server , c . config . Headers )
209
246
if err != nil {
210
247
connerr = err
211
248
continue
@@ -272,13 +309,23 @@ func (c *Client) Close() error {
272
309
func (c * Client ) connectStreams (chans <- chan ssh.NewChannel ) {
273
310
for ch := range chans {
274
311
remote := string (ch .ExtraData ())
312
+ socks := remote == "socks"
313
+ if socks && c .socksServer == nil {
314
+ c .Debugf ("Denied socks request, please enable client socks remote." )
315
+ ch .Reject (ssh .Prohibited , "SOCKS5 is not enabled on the client" )
316
+ continue
317
+ }
275
318
stream , reqs , err := ch .Accept ()
276
319
if err != nil {
277
320
c .Debugf ("Failed to accept stream: %s" , err )
278
321
continue
279
322
}
280
323
go ssh .DiscardRequests (reqs )
281
324
l := c .Logger .Fork ("conn#%d" , c .connStats .New ())
282
- go chshare .HandleTCPStream (l , & c .connStats , stream , remote )
325
+ if socks {
326
+ go chshare .HandleSocksStream (l , c .socksServer , & c .connStats , stream )
327
+ } else {
328
+ go chshare .HandleTCPStream (l , & c .connStats , stream , remote )
329
+ }
283
330
}
284
331
}
0 commit comments