ssh: added timeout parameter (optimize #289 @299)

This commit is contained in:
nadoo 2021-12-09 19:22:12 +08:00
parent ff09c45fb6
commit 5cbfcf815f
7 changed files with 66 additions and 37 deletions

View File

@ -100,7 +100,7 @@ glider -h
<summary>click to see details</summary>
```bash
glider 0.15.0 usage:
glider 0.15.1 usage:
-check string
check=tcp[://HOST:PORT]: tcp port connect check
check=http://HOST[:PORT][/URI][#expect=STRING_IN_RESP_LINE]
@ -205,7 +205,8 @@ SSR scheme:
ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz
SSH scheme:
ssh://user[:pass]@host:port[?key=keypath]
ssh://user[:pass]@host:port[?key=keypath&timeout=SECONDS]
timeout: timeout of ssh handshake and channel operation, default: 5
VMess scheme:
vmess://[security:]uuid@host:port[?alterID=num]

View File

@ -177,7 +177,8 @@ func usage() {
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "SSH scheme:\n")
fmt.Fprintf(w, " ssh://user[:pass]@host:port[?key=keypath]\n")
fmt.Fprintf(w, " ssh://user[:pass]@host:port[?key=keypath&timeout=SECONDS]\n")
fmt.Fprintf(w, " timeout: timeout of ssh handshake and channel operation, default: 5\n")
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "VMess scheme:\n")

View File

@ -109,9 +109,10 @@ listen=socks5://:1080
# forward=ssr://method:pass@1.1.1.1:8443?protocol=auth_aes128_md5&protocol_param=xxx&obfs=tls1.2_ticket_auth&obfs_param=yyy
# ssh forwarder
# forward=ssh://user[:pass]@host:port[?key=keypath]
# forward=ssh://user[:pass]@host:port[?key=keypath&timeout=SECONDS]
# forward=ssh://root:pass@host:port
# forward=ssh://root@host:port?key=/path/to/keyfile
# forward=ssh://root@host:port?key=/path/to/keyfile&timeout=5
# http proxy as forwarder
# forward=http://1.1.1.1:8080

2
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
golang.org/x/net v0.0.0-20211205041911-012df41ee64c // indirect
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b // indirect
)
// Replace dependency modules with local developing copy

4
go.sum
View File

@ -130,8 +130,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c h1:7SfqwP5fxEtl/P02w5IhKc86ziJ+A25yFrkVgoy2FT8=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28=
golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -18,7 +18,7 @@ import (
)
var (
version = "0.15.0"
version = "0.15.1"
config = parseConfig()
)

View File

@ -4,6 +4,7 @@ import (
"net"
"net/url"
"os"
"strconv"
"sync"
"time"
@ -19,9 +20,10 @@ type SSH struct {
proxy proxy.Proxy
addr string
mu sync.Mutex
sshCfg *ssh.ClientConfig
sshClient *ssh.Client
mu sync.Mutex
conn net.Conn
client *ssh.Client
config *ssh.ClientConfig
}
func init() {
@ -42,18 +44,16 @@ func NewSSH(s string, d proxy.Dialer, p proxy.Proxy) (*SSH, error) {
}
config := &ssh.ClientConfig{
User: user,
Timeout: time.Second * 3,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
User: user,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
if pass, _ := u.User.Password(); pass != "" {
config.Auth = []ssh.AuthMethod{ssh.Password(pass)}
}
if key := u.Query().Get("key"); key != "" {
query := u.Query()
if key := query.Get("key"); key != "" {
keyAuth, err := privateKeyAuth(key)
if err != nil {
log.F("[ssh] read key file error: %s", err)
@ -62,11 +62,23 @@ func NewSSH(s string, d proxy.Dialer, p proxy.Proxy) (*SSH, error) {
config.Auth = append(config.Auth, keyAuth)
}
// timeout of ssh handshake and channel operation
qtimeout := query.Get("timeout")
if qtimeout == "" {
qtimeout = "5" // default timeout
}
timeout, err := strconv.ParseUint(qtimeout, 10, 32)
if err != nil {
log.F("[ssh] parse timeout err: %s", err)
return nil, err
}
config.Timeout = time.Second * time.Duration(timeout)
t := &SSH{
dialer: d,
proxy: p,
addr: u.Host,
sshCfg: config,
config: config,
}
if _, port, _ := net.SplitHostPort(t.addr); port == "" {
@ -89,6 +101,32 @@ func (s *SSH) Addr() string {
return s.addr
}
// Dial connects to the address addr on the network net via the proxy.
func (s *SSH) Dial(network, addr string) (net.Conn, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.client != nil {
if c, err := s.dial(network, addr); err == nil {
return c, nil
}
s.conn.Close()
}
if err := s.initConn(); err != nil {
return nil, err
}
return s.dial(network, addr)
}
func (s *SSH) dial(network, addr string) (net.Conn, error) {
s.conn.SetDeadline(time.Now().Add(s.config.Timeout))
c, err := s.client.Dial(network, addr)
s.conn.SetDeadline(time.Time{})
return c, err
}
func (s *SSH) initConn() error {
c, err := s.dialer.Dial("tcp", s.addr)
if err != nil {
@ -96,32 +134,20 @@ func (s *SSH) initConn() error {
return err
}
sshConn, sshChan, sshReq, err := ssh.NewClientConn(c, s.addr, s.sshCfg)
c.SetDeadline(time.Now().Add(s.config.Timeout))
sshConn, sshChan, sshReq, err := ssh.NewClientConn(c, s.addr, s.config)
if err != nil {
log.F("[ssh]: initial connection to %s error: %s", s.addr, err)
c.Close()
return err
}
s.sshClient = ssh.NewClient(sshConn, sshChan, sshReq)
c.SetDeadline(time.Time{})
s.conn = c
s.client = ssh.NewClient(sshConn, sshChan, sshReq)
return nil
}
// Dial connects to the address addr on the network net via the proxy.
func (s *SSH) Dial(network, addr string) (net.Conn, error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.sshClient != nil {
if c, err := s.sshClient.Dial(network, addr); err == nil {
return c, nil
}
s.sshClient.Conn.Close()
}
if err := s.initConn(); err != nil {
return nil, err
}
return s.sshClient.Dial(network, addr)
}
// DialUDP connects to the given address via the proxy.
func (s *SSH) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
return nil, nil, proxy.ErrNotSupported