From 8d16b2d0b5c13121ce97969becd09a318bbb7e68 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Sat, 22 Dec 2018 23:10:55 +0800 Subject: [PATCH] add timeout for Connectors --- .dockerignore | 25 ++++++++++++++++++ chain.go | 7 ++++- client.go | 10 +++++++- common_test.go | 4 +++ forward.go | 15 ++++++++--- gost.go | 6 +++-- http.go | 13 ++++++++++ http_test.go | 5 ++-- socks.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++--- ss.go | 13 ++++++++++ 10 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c121e41 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test +release +debian +docs + +*.exe +*.test + +*.bak + +.git +.gitignore +LICENSE +VERSION +README.md +Changelog.md +Makefile +docker-compose.yml \ No newline at end of file diff --git a/chain.go b/chain.go index ab39560..a3d81d1 100644 --- a/chain.go +++ b/chain.go @@ -136,8 +136,13 @@ func (c *Chain) dialWithOptions(addr string, options *ChainOptions) (net.Conn, e ipAddr := c.resolve(addr, options.Resolver, options.Hosts) + timeout := options.Timeout + if timeout <= 0 { + timeout = DialTimeout + } + if route.IsEmpty() { - return net.DialTimeout("tcp", ipAddr, options.Timeout) + return net.DialTimeout("tcp", ipAddr, timeout) } conn, err := route.getConn() diff --git a/client.go b/client.go index 9b9ae8a..8730916 100644 --- a/client.go +++ b/client.go @@ -236,7 +236,8 @@ func QUICConfigHandshakeOption(config *QUICConfig) HandshakeOption { // ConnectOptions describes the options for Connector.Connect. type ConnectOptions struct { - Addr string + Addr string + Timeout time.Duration } // ConnectOption allows a common way to set ConnectOptions. @@ -248,3 +249,10 @@ func AddrConnectOption(addr string) ConnectOption { opts.Addr = addr } } + +// TimeoutConnectOption specifies the timeout for connecting to target. +func TimeoutConnectOption(timeout time.Duration) ConnectOption { + return func(opts *ConnectOptions) { + opts.Timeout = timeout + } +} diff --git a/common_test.go b/common_test.go index 745c883..1665fb6 100644 --- a/common_test.go +++ b/common_test.go @@ -8,11 +8,15 @@ import ( "net" "net/http" "sync" + "time" ) func init() { // SetLogger(&LogLogger{}) // Debug = true + DialTimeout = 500 * time.Millisecond + HandshakeTimeout = 500 * time.Millisecond + ConnectTimeout = 500 * time.Millisecond cert, err := GenCertificate() if err != nil { diff --git a/forward.go b/forward.go index d5bc64e..7f16144 100644 --- a/forward.go +++ b/forward.go @@ -714,6 +714,8 @@ func (l *tcpRemoteForwardListener) listenLoop() { continue } + tempDelay = 0 + select { case l.connChan <- conn: default: @@ -774,7 +776,7 @@ func (l *tcpRemoteForwardListener) muxAccept() (conn net.Conn, err error) { return cc, nil } -func (l *tcpRemoteForwardListener) getSession() (*muxSession, error) { +func (l *tcpRemoteForwardListener) getSession() (s *muxSession, err error) { l.sessionMux.Lock() defer l.sessionMux.Unlock() @@ -787,6 +789,15 @@ func (l *tcpRemoteForwardListener) getSession() (*muxSession, error) { return nil, err } + defer func(c net.Conn) { + if err != nil { + c.Close() + } + }(conn) + + conn.SetDeadline(time.Now().Add(HandshakeTimeout)) + defer conn.SetDeadline(time.Time{}) + conn, err = socks5Handshake(conn, l.chain.LastNode().User) if err != nil { return nil, err @@ -797,13 +808,11 @@ func (l *tcpRemoteForwardListener) getSession() (*muxSession, error) { return nil, err } - conn.SetReadDeadline(time.Now().Add(ReadTimeout)) rep, err := gosocks5.ReadReply(conn) if err != nil { log.Log("[rtcp] SOCKS5 BIND reply: ", err) return nil, err } - conn.SetReadDeadline(time.Time{}) if rep.Rep != gosocks5.Succeeded { log.Logf("[rtcp] bind on %s failure", l.addr) return nil, fmt.Errorf("Bind on %s failure", l.addr.String()) diff --git a/gost.go b/gost.go index ab222b3..5570254 100644 --- a/gost.go +++ b/gost.go @@ -53,10 +53,12 @@ var ( DialTimeout = 5 * time.Second // HandshakeTimeout is the timeout of handshake. HandshakeTimeout = 5 * time.Second + // ConnectTimeout is the timeout for connect. + ConnectTimeout = 5 * time.Second // ReadTimeout is the timeout for reading. - ReadTimeout = 5 * time.Second + ReadTimeout = 10 * time.Second // WriteTimeout is the timeout for writing. - WriteTimeout = 5 * time.Second + WriteTimeout = 10 * time.Second // PingTimeout is the timeout for pinging. PingTimeout = 30 * time.Second // PingRetries is the reties of ping. diff --git a/http.go b/http.go index ccc29bd..43d0f69 100644 --- a/http.go +++ b/http.go @@ -28,6 +28,19 @@ func HTTPConnector(user *url.Userinfo) Connector { } func (c *httpConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + req := &http.Request{ Method: http.MethodConnect, URL: &url.URL{Host: addr}, diff --git a/http_test.go b/http_test.go index b322672..461d21c 100644 --- a/http_test.go +++ b/http_test.go @@ -12,7 +12,6 @@ import ( "net/http/httptest" "net/url" "testing" - "time" ) // proxyConn obtains a connection to the proxy server. @@ -77,8 +76,8 @@ func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byt return } - conn.SetDeadline(time.Now().Add(500 * time.Millisecond)) - defer conn.SetDeadline(time.Time{}) + // conn.SetDeadline(time.Now().Add(500 * time.Millisecond)) + // defer conn.SetDeadline(time.Time{}) conn, err = client.Connect(conn, u.Host) if err != nil { diff --git a/socks.go b/socks.go index 16ce22f..38a4533 100644 --- a/socks.go +++ b/socks.go @@ -205,6 +205,19 @@ func SOCKS5Connector(user *url.Userinfo) Connector { } func (c *socks5Connector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + selector := &clientSelector{ TLSConfig: &tls.Config{InsecureSkipVerify: true}, User: c.User, @@ -266,6 +279,19 @@ func SOCKS5BindConnector(user *url.Userinfo) Connector { } func (c *socks5BindConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + cc, err := socks5Handshake(conn, c.User) if err != nil { return nil, err @@ -292,12 +318,10 @@ func (c *socks5BindConnector) Connect(conn net.Conn, addr string, options ...Con log.Log("[socks5] bind\n", req) } - conn.SetReadDeadline(time.Now().Add(ReadTimeout)) reply, err := gosocks5.ReadReply(conn) if err != nil { return nil, err } - conn.SetReadDeadline(time.Time{}) if Debug { log.Log("[socks5] bind\n", reply) @@ -327,6 +351,19 @@ func SOCKS5UDPConnector(user *url.Userinfo) Connector { } func (c *socks5UDPConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + cc, err := socks5Handshake(conn, c.User) if err != nil { return nil, err @@ -388,6 +425,19 @@ func SOCKS4Connector() Connector { } func (c *socks4Connector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + taddr, err := net.ResolveTCPAddr("tcp4", addr) if err != nil { return nil, err @@ -435,6 +485,19 @@ func SOCKS4AConnector() Connector { } func (c *socks4aConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -1645,7 +1708,7 @@ type socks5UDPConn struct { func (c *socks5UDPConn) Read(b []byte) (int, error) { data := mPool.Get().([]byte) defer mPool.Put(data) - + n, err := c.UDPConn.Read(data) if err != nil { return 0, err diff --git a/ss.go b/ss.go index b6d8984..e492f51 100644 --- a/ss.go +++ b/ss.go @@ -68,6 +68,19 @@ func ShadowConnector(cipher *url.Userinfo) Connector { } func (c *shadowConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) { + opts := &ConnectOptions{} + for _, option := range options { + option(opts) + } + + timeout := opts.Timeout + if timeout <= 0 { + timeout = ConnectTimeout + } + + conn.SetDeadline(time.Now().Add(timeout)) + defer conn.SetDeadline(time.Time{}) + rawaddr, err := ss.RawAddr(addr) if err != nil { return nil, err