From 5cbfcf815fa256e3b3caaa8b5478d3445d50b496 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Thu, 9 Dec 2021 19:22:12 +0800 Subject: [PATCH] ssh: added timeout parameter (optimize #289 @299) --- README.md | 5 ++- config.go | 3 +- config/glider.conf.example | 3 +- go.mod | 2 +- go.sum | 4 +- main.go | 2 +- proxy/ssh/ssh.go | 84 +++++++++++++++++++++++++------------- 7 files changed, 66 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 062450d..a6c3c36 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ glider -h click to see details ```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] diff --git a/config.go b/config.go index 9491035..81492d4 100644 --- a/config.go +++ b/config.go @@ -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") diff --git a/config/glider.conf.example b/config/glider.conf.example index c31f0ae..2549c77 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -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 diff --git a/go.mod b/go.mod index 93e1f6b..3f5adc3 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index c679173..833e660 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/main.go b/main.go index 0f2d8a4..5c88e11 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "0.15.0" + version = "0.15.1" config = parseConfig() ) diff --git a/proxy/ssh/ssh.go b/proxy/ssh/ssh.go index 591a0a4..0bc9053 100644 --- a/proxy/ssh/ssh.go +++ b/proxy/ssh/ssh.go @@ -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