Skip to content

Commit a16c592

Browse files
committed
This commit implements Feature Request #59
Now we can use the following command to connect to an SSH server through a chisel tunnel. ```bash ssh -o ProxyCommand='/path/to/chisel client https://chisel.boleyn.su stdio:%h:%p' me@boleyn.su ```
1 parent d9c45c6 commit a16c592

6 files changed

Lines changed: 80 additions & 7 deletions

File tree

client/client.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package chclient
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"net"
@@ -68,11 +69,18 @@ func NewClient(config *Config) (*Client, error) {
6869
//swap to websockets scheme
6970
u.Scheme = strings.Replace(u.Scheme, "http", "ws", 1)
7071
shared := &chshare.Config{}
72+
stdioCnt := 0
7173
for _, s := range config.Remotes {
7274
r, err := chshare.DecodeRemote(s)
7375
if err != nil {
7476
return nil, fmt.Errorf("Failed to decode remote '%s': %s", s, err)
7577
}
78+
if r.Stdio {
79+
stdioCnt++
80+
}
81+
if stdioCnt > 1 {
82+
return nil, errors.New("Only one stdio is allowed")
83+
}
7684
shared.Remotes = append(shared.Remotes, r)
7785
}
7886
config.shared = shared

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ var clientHelp = `
224224
socks
225225
5000:socks
226226
R:2222:localhost:22
227+
stdio:example.com:22
227228
228229
When the chisel server has --socks5 enabled, remotes can
229230
specify "socks" in place of remote-host and remote-port.
@@ -236,6 +237,13 @@ var clientHelp = `
236237
is, the server will listen and accept connections, and they
237238
will be proxied through the client which specified the remote.
238239
240+
When stdio is used as local-host, the tunnel will connect standard
241+
input/output of this program with the remote. This is useful when
242+
combined with ssh ProxyCommand. You can use
243+
ssh -o ProxyCommand='chisel client chiselserver stdio:%h:%p' \
244+
user@example.com
245+
to connect to an SSH server through the tunnel.
246+
239247
Options:
240248
241249
--fingerprint, A *strongly recommended* fingerprint string

share/logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func NewLogger(prefix string) *Logger {
2020
func NewLoggerFlag(prefix string, flag int) *Logger {
2121
l := &Logger{
2222
prefix: prefix,
23-
logger: log.New(os.Stdout, "", flag),
23+
logger: log.New(os.Stderr, "", flag),
2424
Info: false,
2525
Debug: false,
2626
}

share/proxy.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,25 @@ func NewTCPProxy(logger *Logger, ssh GetSSHConn, index int, remote *Remote) *TCP
3131
}
3232

3333
func (p *TCPProxy) Start(ctx context.Context) error {
34-
l, err := net.Listen("tcp4", p.remote.LocalHost+":"+p.remote.LocalPort)
35-
if err != nil {
36-
return fmt.Errorf("%s: %s", p.Logger.Prefix(), err)
34+
if p.remote.Stdio {
35+
go func() {
36+
for {
37+
p.accept(Stdio)
38+
select {
39+
case <-ctx.Done():
40+
return
41+
default:
42+
// the connection is not ready yet
43+
}
44+
}
45+
}()
46+
} else {
47+
l, err := net.Listen("tcp4", p.remote.LocalHost+":"+p.remote.LocalPort)
48+
if err != nil {
49+
return fmt.Errorf("%s: %s", p.Logger.Prefix(), err)
50+
}
51+
go p.listen(ctx, l)
3752
}
38-
go p.listen(ctx, l)
3953
return nil
4054
}
4155

share/remote.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ import (
2020
// 192.168.0.1:3000:google.com:80 ->
2121
// local 192.168.0.1:3000
2222
// remote google.com:80
23+
// 127.0.0.1:1080:socks
24+
// local 127.0.0.1:1080
25+
// remote socks
26+
// stdio:example.com:22
27+
// local stdio
28+
// remote example.com:22
2329

2430
type Remote struct {
2531
LocalHost, LocalPort, RemoteHost, RemotePort string
26-
Socks, Reverse bool
32+
Socks, Reverse, Stdio bool
2733
}
2834

2935
const revPrefix = "R:"
@@ -51,6 +57,10 @@ func DecodeRemote(s string) (*Remote, error) {
5157
r.Socks = true
5258
continue
5359
}
60+
if i == 0 && p == "stdio" {
61+
r.Stdio = true
62+
continue
63+
}
5464
if isPort(p) {
5565
if !r.Socks && r.RemotePort == "" {
5666
r.RemotePort = p
@@ -85,6 +95,9 @@ func DecodeRemote(s string) (*Remote, error) {
8595
if !r.Socks && r.RemoteHost == "" {
8696
r.RemoteHost = "0.0.0.0"
8797
}
98+
if r.Stdio && r.Reverse {
99+
return nil, errors.New("stdio cannot be used with reverse tunnel")
100+
}
88101
return r, nil
89102
}
90103

@@ -111,7 +124,14 @@ func (r *Remote) String() string {
111124
if r.Reverse {
112125
tag = revPrefix
113126
}
114-
return tag + r.LocalHost + ":" + r.LocalPort + "=>" + r.Remote()
127+
return tag + r.Local() + "=>" + r.Remote()
128+
}
129+
130+
func (r *Remote) Local() string {
131+
if r.Stdio {
132+
return "stdio"
133+
}
134+
return r.LocalHost + ":" + r.LocalPort
115135
}
116136

117137
func (r *Remote) Remote() string {

share/stdio.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package chshare
2+
3+
import (
4+
"os"
5+
)
6+
7+
8+
type stdio struct {}
9+
10+
var Stdio = &stdio{}
11+
12+
func (t *stdio) Read(b []byte) (n int, err error) {
13+
return os.Stdin.Read(b)
14+
}
15+
16+
func (t *stdio) Write(b []byte) (n int, err error) {
17+
return os.Stdout.Write(b)
18+
}
19+
20+
// We do not close stdio
21+
func (t *stdio) Close() error {
22+
return nil
23+
}

0 commit comments

Comments
 (0)