Skip to content

Commit 68050d0

Browse files
committed
merge client reverse+socks PR by @aus (closes jpillora#78)
- @aus's branch also includes PR for custom headers, thanks @AkeemMcLennon (closes jpillora#90) - support client connections via socks - add more architectures to the built releases - remove vendor (go.sum will enforce correct deps)
2 parents d9c45c6 + f360e46 commit 68050d0

File tree

11 files changed

+220
-103
lines changed

11 files changed

+220
-103
lines changed

.github/goreleaser.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ builds:
1212
- amd64
1313
- arm
1414
- arm64
15+
- ppc64
16+
- ppc64le
17+
- mips
18+
- mipsle
19+
- mips64
20+
- mips64le
21+
- mips64p32
22+
- mips64p32le
23+
- ppc
24+
- s390
25+
- s390x
1526
goarm:
1627
- 6
1728
- 7

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,14 @@ $ chisel client --help
212212
--max-retry-interval, Maximum wait time before retrying after a
213213
disconnection. Defaults to 5 minutes.
214214
215-
--proxy, An optional HTTP CONNECT proxy which will be used reach
216-
the chisel server. Authentication can be specified inside the URL.
215+
--proxy, An optional HTTP CONNECT or SOCKS5 proxy which will be
216+
used to reach the chisel server. Authentication can be specified
217+
inside the URL.
217218
For example, http://admin:[email protected]:8081
219+
or: socks://admin:[email protected]:1080
220+
221+
--header, Set a custom header in the form "HeaderName: HeaderContent".
222+
Can be used multiple times. (e.g --header "Foo: Bar" --header "Hello: World")
218223
219224
--hostname, Optionally set the 'Host' header (defaults to the host
220225
defined in the endpoint url).

client/client.go

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import (
1111
"strings"
1212
"time"
1313

14+
"github.com/armon/go-socks5"
1415
"github.com/gorilla/websocket"
1516
"github.com/jpillora/backoff"
1617
chshare "github.com/jpillora/chisel/share"
18+
1719
"golang.org/x/crypto/ssh"
20+
"golang.org/x/net/proxy"
1821
)
1922

2023
//Config represents a client configuration
@@ -26,22 +29,24 @@ type Config struct {
2629
MaxRetryCount int
2730
MaxRetryInterval time.Duration
2831
Server string
29-
HTTPProxy string
32+
Proxy string
3033
Remotes []string
31-
HostHeader string
34+
Headers http.Header
35+
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
3236
}
3337

3438
//Client represents a client instance
3539
type Client struct {
3640
*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
4550
}
4651

4752
//NewClient creates a new client instance
@@ -68,11 +73,15 @@ func NewClient(config *Config) (*Client, error) {
6873
//swap to websockets scheme
6974
u.Scheme = strings.Replace(u.Scheme, "http", "ws", 1)
7075
shared := &chshare.Config{}
76+
createSocksServer := false
7177
for _, s := range config.Remotes {
7278
r, err := chshare.DecodeRemote(s)
7379
if err != nil {
7480
return nil, fmt.Errorf("Failed to decode remote '%s': %s", s, err)
7581
}
82+
if r.Socks && r.Reverse {
83+
createSocksServer = true
84+
}
7685
shared.Remotes = append(shared.Remotes, r)
7786
}
7887
config.shared = shared
@@ -85,8 +94,8 @@ func NewClient(config *Config) (*Client, error) {
8594
}
8695
client.Info = true
8796

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)
9099
if err != nil {
91100
return nil, fmt.Errorf("Invalid proxy URL (%s)", err)
92101
}
@@ -102,6 +111,14 @@ func NewClient(config *Config) (*Client, error) {
102111
Timeout: 30 * time.Second,
103112
}
104113

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+
105122
return client, nil
106123
}
107124

@@ -129,8 +146,8 @@ func (c *Client) verifyServer(hostname string, remote net.Addr, key ssh.PublicKe
129146
//Start client and does not block
130147
func (c *Client) Start(ctx context.Context) error {
131148
via := ""
132-
if c.httpProxyURL != nil {
133-
via = " via " + c.httpProxyURL.String()
149+
if c.proxyURL != nil {
150+
via = " via " + c.proxyURL.String()
134151
}
135152
//prepare non-reverse proxies
136153
for i, r := range c.config.shared.Remotes {
@@ -192,20 +209,40 @@ func (c *Client) connectionLoop() {
192209
WriteBufferSize: 1024,
193210
HandshakeTimeout: 45 * time.Second,
194211
Subprotocols: []string{chshare.ProtocolVersion},
212+
NetDialContext: c.config.DialContext,
195213
}
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+
}
206243
}
207244
}
208-
wsConn, _, err := d.Dial(c.server, wsHeaders)
245+
wsConn, _, err := d.Dial(c.server, c.config.Headers)
209246
if err != nil {
210247
connerr = err
211248
continue
@@ -272,13 +309,23 @@ func (c *Client) Close() error {
272309
func (c *Client) connectStreams(chans <-chan ssh.NewChannel) {
273310
for ch := range chans {
274311
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+
}
275318
stream, reqs, err := ch.Accept()
276319
if err != nil {
277320
c.Debugf("Failed to accept stream: %s", err)
278321
continue
279322
}
280323
go ssh.DiscardRequests(reqs)
281324
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+
}
283330
}
284331
}

client/client_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package chclient
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
"time"
9+
)
10+
11+
func TestCustomHeaders(t *testing.T) {
12+
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
13+
if req.Header.Get("Foo") != "Bar" {
14+
t.Fatal("expected header Foo to be 'Bar'")
15+
}
16+
}))
17+
// Close the server when test finishes
18+
defer server.Close()
19+
headers := http.Header{}
20+
headers.Set("Foo", "Bar")
21+
config := Config{
22+
Fingerprint: "",
23+
Auth: "",
24+
KeepAlive: time.Second,
25+
MaxRetryCount: 0,
26+
MaxRetryInterval: time.Second,
27+
Server: server.URL,
28+
Remotes: []string{"socks"},
29+
Headers: headers,
30+
}
31+
c, err := NewClient(&config)
32+
if err != nil {
33+
log.Fatal(err)
34+
}
35+
if err = c.Run(); err != nil {
36+
log.Fatal(err)
37+
}
38+
}

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/jpillora/requestlog v1.0.0
1313
github.com/jpillora/sizestr v1.0.0
1414
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
15-
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
16-
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
17-
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
15+
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
16+
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
17+
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect
1818
)

go.sum

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,30 @@ github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZ
22
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
33
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
44
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
5-
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
6-
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
75
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
86
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
9-
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
10-
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
117
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
128
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
13-
github.com/jpillora/ansi v0.0.0-20170202005112-f496b27cd669 h1:l5rH/CnVVu+HPxjtxjM90nHrm4nov3j3RF9/62UjgLs=
14-
github.com/jpillora/ansi v0.0.0-20170202005112-f496b27cd669/go.mod h1:kOeLNvjNBGSV3uYtFjvb72+fnZCMFJF1XDvRIjdom0g=
159
github.com/jpillora/ansi v1.0.2 h1:+Ei5HCAH0xsrQRCT2PDr4mq9r4Gm4tg+arNdXRkB22s=
1610
github.com/jpillora/ansi v1.0.2/go.mod h1:D2tT+6uzJvN1nBVQILYWkIdq7zG+b5gcFN5WI/VyjMY=
17-
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME=
18-
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
1911
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
2012
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
21-
github.com/jpillora/requestlog v0.0.0-20181015073026-df8817be5f82 h1:7ufdyC3aMxFcCv+ABZy/dmIVGKFoGNBCqOgLYPIckD8=
22-
github.com/jpillora/requestlog v0.0.0-20181015073026-df8817be5f82/go.mod h1:w8buj+yNfmLEP0ENlbG/FRnK6bVmuhqXnukYCs9sDvY=
2313
github.com/jpillora/requestlog v1.0.0 h1:bg++eJ74T7DYL3DlIpiwknrtfdUA9oP/M4fL+PpqnyA=
2414
github.com/jpillora/requestlog v1.0.0/go.mod h1:HTWQb7QfDc2jtHnWe2XEIEeJB7gJPnVdpNn52HXPvy8=
25-
github.com/jpillora/sizestr v0.0.0-20160130011556-e2ea2fa42fb9 h1:0c9jcgBtHRtDU//jTrcCgWG6UHjMZytiq/3WhraNgUM=
26-
github.com/jpillora/sizestr v0.0.0-20160130011556-e2ea2fa42fb9/go.mod h1:1ffp+CRe0eAwwRb0/BownUAjMBsmTLwgAvRbfj9dRwE=
2715
github.com/jpillora/sizestr v1.0.0 h1:4tr0FLxs1Mtq3TnsLDV+GYUWG7Q26a6s+tV5Zfw2ygw=
2816
github.com/jpillora/sizestr v1.0.0/go.mod h1:bUhLv4ctkknatr6gR42qPxirmd5+ds1u7mzD+MZ33f0=
2917
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
3018
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
31-
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e h1:IzypfodbhbnViNUO/MEh0FzCUooG97cIGfdggUrUSyU=
32-
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
3319
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
34-
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
35-
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
36-
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f h1:4pRM7zYwpBjCnfA1jRmhItLxYJkaEnsmuAcRtA347DA=
37-
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
20+
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
21+
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
3822
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
39-
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
40-
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
41-
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8 h1:R91KX5nmbbvEd7w370cbVzKC+EzCTGqZq63Zad5IcLM=
42-
golang.org/x/sys v0.0.0-20181019160139-8e24a49d80f8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
23+
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
24+
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
4325
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
4426
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4527
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4628
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
47-
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
48-
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
29+
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
30+
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4931
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

0 commit comments

Comments
 (0)