mirror of
				https://github.com/nadoo/glider.git
				synced 2025-11-04 07:42:38 +08:00 
			
		
		
		
	ssh: added timeout parameter (optimize #289 @299)
This commit is contained in:
		
							parent
							
								
									ff09c45fb6
								
							
						
					
					
						commit
						5cbfcf815f
					
				@ -100,7 +100,7 @@ glider -h
 | 
				
			|||||||
<summary>click to see details</summary>
 | 
					<summary>click to see details</summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
glider 0.15.0 usage:
 | 
					glider 0.15.1 usage:
 | 
				
			||||||
  -check string
 | 
					  -check string
 | 
				
			||||||
    	check=tcp[://HOST:PORT]: tcp port connect check
 | 
					    	check=tcp[://HOST:PORT]: tcp port connect check
 | 
				
			||||||
    	check=http://HOST[:PORT][/URI][#expect=STRING_IN_RESP_LINE]
 | 
					    	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
 | 
					  ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SSH scheme:
 | 
					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 scheme:
 | 
				
			||||||
  vmess://[security:]uuid@host:port[?alterID=num]
 | 
					  vmess://[security:]uuid@host:port[?alterID=num]
 | 
				
			||||||
 | 
				
			|||||||
@ -177,7 +177,8 @@ func usage() {
 | 
				
			|||||||
	fmt.Fprintf(w, "\n")
 | 
						fmt.Fprintf(w, "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(w, "SSH scheme:\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, "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(w, "VMess scheme:\n")
 | 
						fmt.Fprintf(w, "VMess scheme:\n")
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
					# 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
 | 
					# 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:pass@host:port
 | 
				
			||||||
# forward=ssh://root@host:port?key=/path/to/keyfile
 | 
					# forward=ssh://root@host:port?key=/path/to/keyfile
 | 
				
			||||||
 | 
					# forward=ssh://root@host:port?key=/path/to/keyfile&timeout=5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# http proxy as forwarder
 | 
					# http proxy as forwarder
 | 
				
			||||||
# forward=http://1.1.1.1:8080
 | 
					# forward=http://1.1.1.1:8080
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@ -26,7 +26,7 @@ require (
 | 
				
			|||||||
	github.com/templexxx/xorsimd v0.4.1 // indirect
 | 
						github.com/templexxx/xorsimd v0.4.1 // indirect
 | 
				
			||||||
	github.com/tjfoc/gmsm v1.4.1 // indirect
 | 
						github.com/tjfoc/gmsm v1.4.1 // indirect
 | 
				
			||||||
	github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // 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
 | 
					// Replace dependency modules with local developing copy
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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-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-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-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-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28=
 | 
				
			||||||
golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
					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/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-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							@ -18,7 +18,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	version = "0.15.0"
 | 
						version = "0.15.1"
 | 
				
			||||||
	config  = parseConfig()
 | 
						config  = parseConfig()
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,9 +20,10 @@ type SSH struct {
 | 
				
			|||||||
	proxy  proxy.Proxy
 | 
						proxy  proxy.Proxy
 | 
				
			||||||
	addr   string
 | 
						addr   string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mu        sync.Mutex
 | 
						mu     sync.Mutex
 | 
				
			||||||
	sshCfg    *ssh.ClientConfig
 | 
						conn   net.Conn
 | 
				
			||||||
	sshClient *ssh.Client
 | 
						client *ssh.Client
 | 
				
			||||||
 | 
						config *ssh.ClientConfig
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@ -42,18 +44,16 @@ func NewSSH(s string, d proxy.Dialer, p proxy.Proxy) (*SSH, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config := &ssh.ClientConfig{
 | 
						config := &ssh.ClientConfig{
 | 
				
			||||||
		User:    user,
 | 
							User:            user,
 | 
				
			||||||
		Timeout: time.Second * 3,
 | 
							HostKeyCallback: ssh.InsecureIgnoreHostKey(),
 | 
				
			||||||
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pass, _ := u.User.Password(); pass != "" {
 | 
						if pass, _ := u.User.Password(); pass != "" {
 | 
				
			||||||
		config.Auth = []ssh.AuthMethod{ssh.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)
 | 
							keyAuth, err := privateKeyAuth(key)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.F("[ssh] read key file error: %s", err)
 | 
								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)
 | 
							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{
 | 
						t := &SSH{
 | 
				
			||||||
		dialer: d,
 | 
							dialer: d,
 | 
				
			||||||
		proxy:  p,
 | 
							proxy:  p,
 | 
				
			||||||
		addr:   u.Host,
 | 
							addr:   u.Host,
 | 
				
			||||||
		sshCfg: config,
 | 
							config: config,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, port, _ := net.SplitHostPort(t.addr); port == "" {
 | 
						if _, port, _ := net.SplitHostPort(t.addr); port == "" {
 | 
				
			||||||
@ -89,6 +101,32 @@ func (s *SSH) Addr() string {
 | 
				
			|||||||
	return s.addr
 | 
						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 {
 | 
					func (s *SSH) initConn() error {
 | 
				
			||||||
	c, err := s.dialer.Dial("tcp", s.addr)
 | 
						c, err := s.dialer.Dial("tcp", s.addr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@ -96,32 +134,20 @@ func (s *SSH) initConn() error {
 | 
				
			|||||||
		return err
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		log.F("[ssh]: initial connection to %s error: %s", s.addr, err)
 | 
							log.F("[ssh]: initial connection to %s error: %s", s.addr, err)
 | 
				
			||||||
 | 
							c.Close()
 | 
				
			||||||
		return err
 | 
							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
 | 
						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.
 | 
					// DialUDP connects to the given address via the proxy.
 | 
				
			||||||
func (s *SSH) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
 | 
					func (s *SSH) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
 | 
				
			||||||
	return nil, nil, proxy.ErrNotSupported
 | 
						return nil, nil, proxy.ErrNotSupported
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user