Skip to content

Commit bd9460b

Browse files
fix(RDGRS-662): server side keep alive (#19)
* fix(RDGRS-662): server side keep alive From chisel#442 . Not tested * feat(RDGRS-662): build image (#20) (#21) * feat(RDGRS-662): build image * feat(RDGRS-662): restore test CI and dependabot * feat(RDGRS-662): test CI only on linux * feat(RDGRS-662): add version to goreleaser * Revert "feat(RDGRS-662): build image (#20) (#21)" This reverts commit a0feabe. * feat(RDGRS-662): add explicit timer stop
1 parent 22e6564 commit bd9460b

File tree

1 file changed

+50
-18
lines changed

1 file changed

+50
-18
lines changed

share/tunnel/tunnel.go

+50-18
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
"golang.org/x/sync/errgroup"
1919
)
2020

21-
//Config a Tunnel
21+
// Config a Tunnel
2222
type Config struct {
2323
*cio.Logger
2424
Inbound bool
@@ -27,13 +27,13 @@ type Config struct {
2727
KeepAlive time.Duration
2828
}
2929

30-
//Tunnel represents an SSH tunnel with proxy capabilities.
31-
//Both chisel client and server are Tunnels.
32-
//chisel client has a single set of remotes, whereas
33-
//chisel server has multiple sets of remotes (one set per client).
34-
//Each remote has a 1:1 mapping to a proxy.
35-
//Proxies listen, send data over ssh, and the other end of the ssh connection
36-
//communicates with the endpoint and returns the response.
30+
// Tunnel represents an SSH tunnel with proxy capabilities.
31+
// Both chisel client and server are Tunnels.
32+
// chisel client has a single set of remotes, whereas
33+
// chisel server has multiple sets of remotes (one set per client).
34+
// Each remote has a 1:1 mapping to a proxy.
35+
// Proxies listen, send data over ssh, and the other end of the ssh connection
36+
// communicates with the endpoint and returns the response.
3737
type Tunnel struct {
3838
Config
3939
//ssh connection
@@ -47,7 +47,7 @@ type Tunnel struct {
4747
socksServer *socks5.Server
4848
}
4949

50-
//New Tunnel from the given Config
50+
// New Tunnel from the given Config
5151
func New(c Config) *Tunnel {
5252
c.Logger = c.Logger.Fork("tun")
5353
t := &Tunnel{
@@ -68,7 +68,7 @@ func New(c Config) *Tunnel {
6868
return t
6969
}
7070

71-
//BindSSH provides an active SSH for use for tunnelling
71+
// BindSSH provides an active SSH for use for tunnelling
7272
func (t *Tunnel) BindSSH(ctx context.Context, c ssh.Conn, reqs <-chan *ssh.Request, chans <-chan ssh.NewChannel) error {
7373
//link ctx to ssh-conn
7474
go func() {
@@ -104,7 +104,7 @@ func (t *Tunnel) BindSSH(ctx context.Context, c ssh.Conn, reqs <-chan *ssh.Reque
104104
return err
105105
}
106106

107-
//getSSH blocks while connecting
107+
// getSSH blocks while connecting
108108
func (t *Tunnel) getSSH(ctx context.Context) ssh.Conn {
109109
//cancelled already?
110110
if isDone(ctx) {
@@ -140,8 +140,8 @@ func (t *Tunnel) activatingConnWait() <-chan struct{} {
140140
return ch
141141
}
142142

143-
//BindRemotes converts the given remotes into proxies, and blocks
144-
//until the caller cancels the context or there is a proxy error.
143+
// BindRemotes converts the given remotes into proxies, and blocks
144+
// until the caller cancels the context or there is a proxy error.
145145
func (t *Tunnel) BindRemotes(ctx context.Context, remotes []*settings.Remote) error {
146146
if len(remotes) == 0 {
147147
return errors.New("no remotes")
@@ -174,16 +174,48 @@ func (t *Tunnel) BindRemotes(ctx context.Context, remotes []*settings.Remote) er
174174

175175
func (t *Tunnel) keepAliveLoop(sshConn ssh.Conn) {
176176
//ping forever
177+
178+
//reply_timeout set to KeepAlive interval, if KeepAlive is less than 10s, set reply_timeout to 10s
179+
//maybe a new config option for reply_timeout is also fine
180+
reply_timeout := t.Config.KeepAlive
181+
182+
if reply_timeout < 10*time.Second {
183+
reply_timeout = 10 * time.Second
184+
}
185+
177186
for {
178187
time.Sleep(t.Config.KeepAlive)
179-
_, b, err := sshConn.SendRequest("ping", true, nil)
188+
189+
errChannel := make(chan error, 2)
190+
191+
timeoutTimer := time.AfterFunc(reply_timeout, func() {
192+
errChannel <- errors.New("KEEPALIVE REPLY TIMEOUT ERROR")
193+
})
194+
195+
go func() {
196+
_, b, err := sshConn.SendRequest("ping", true, nil)
197+
198+
ret_err := err
199+
200+
if err == nil && len(b) > 0 && !bytes.Equal(b, []byte("pong")) {
201+
t.Debugf("strange ping response")
202+
ret_err = errors.New("strange ping response")
203+
}
204+
205+
errChannel <- ret_err
206+
}()
207+
208+
err := <-errChannel
209+
210+
// explicitly stop the timer, as it's no longer needed at this point.
211+
timeoutTimer.Stop()
212+
// errChannel should be garbage collected, as it won't be in use, even on a timeout,
213+
// as SendRequest will be unblocked on connection closure, the goroutine will send
214+
// an error message and finish, therefore releasing the channel.
215+
180216
if err != nil {
181217
break
182218
}
183-
if len(b) > 0 && !bytes.Equal(b, []byte("pong")) {
184-
t.Debugf("strange ping response")
185-
break
186-
}
187219
}
188220
//close ssh connection on abnormal ping
189221
sshConn.Close()

0 commit comments

Comments
 (0)