2023-11-19 13:06:38 +03:00
|
|
|
package sshtun
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh"
|
|
|
|
)
|
|
|
|
|
|
|
|
// keepAlive periodically sends messages to invoke a response.
|
|
|
|
// If the server does not respond after some period of time,
|
|
|
|
// assume that the underlying net.Conn abruptly died.
|
|
|
|
func (t *Tunnel) keepAlive(ctx context.Context, client *ssh.Client, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
2023-11-21 18:55:04 +03:00
|
|
|
if t.tunConfig.KeepAliveInterval <= 0 || t.tunConfig.KeepAliveMax <= 0 {
|
2023-11-19 13:06:38 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect when the SSH connection is closed.
|
|
|
|
wait := make(chan error, 1)
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
wait <- client.Wait()
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Repeatedly check if the remote server is still alive.
|
|
|
|
var aliveCount int32
|
2023-11-19 17:03:12 +03:00
|
|
|
ticker := time.NewTicker(time.Duration(t.tunConfig.KeepAliveInterval) * time.Second)
|
2023-11-19 13:06:38 +03:00
|
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
t.log.Debug("stopping keep-alive...")
|
|
|
|
_ = client.Close()
|
|
|
|
return
|
|
|
|
case err := <-wait:
|
|
|
|
if err != nil && err != io.EOF {
|
|
|
|
t.log.Error("ssh error:", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
case <-ticker.C:
|
2023-11-19 17:03:12 +03:00
|
|
|
if n := atomic.AddInt32(&aliveCount, 1); n > int32(t.tunConfig.KeepAliveMax) {
|
2023-11-19 13:06:38 +03:00
|
|
|
t.log.Error("keep-alive failed, closing connection...")
|
|
|
|
_ = client.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
_, _, err := client.SendRequest("keepalive@openssh.com", true, nil)
|
|
|
|
if err == nil {
|
|
|
|
atomic.StoreInt32(&aliveCount, 0)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|