diff --git a/README.md b/README.md index c0251d9..fcb670d 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ we can set up local listeners as proxy servers, and forward requests to internet Binary Download: - [https://github.com/nadoo/glider/releases](https://github.com/nadoo/glider/releases) -Build from source code (requires **Go 1.14+** ): +Build from source code (requires **Go 1.15+** ): ```bash git clone https://github.com/nadoo/glider cd glider && go build diff --git a/common/conn/conn.go b/common/conn/conn.go index e156fd4..e697bb2 100644 --- a/common/conn/conn.go +++ b/common/conn/conn.go @@ -2,22 +2,25 @@ package conn import ( "bufio" + "errors" "io" "net" + "os" + "sync" "time" "github.com/nadoo/glider/common/pool" ) const ( - // TCPBufSize is the size of tcp buffer + // TCPBufSize is the size of tcp buffer. TCPBufSize = 16 << 10 - // UDPBufSize is the size of udp buffer + // UDPBufSize is the size of udp buffer. UDPBufSize = 64 << 10 ) -// Conn is a base conn struct +// Conn is a connection with buffered reader. type Conn struct { r *bufio.Reader net.Conn @@ -43,35 +46,39 @@ func (c *Conn) Reader() *bufio.Reader { } // Relay relays between left and right. -func Relay(left, right net.Conn) (int64, int64, error) { - type res struct { - N int64 - Err error - } - ch := make(chan res) +func Relay(left, right net.Conn) error { + var err, err1 error + var wg sync.WaitGroup + var wait = 5 * time.Second + wg.Add(1) go func() { - b := pool.GetBuffer(TCPBufSize) - n, err := io.CopyBuffer(right, left, b) - pool.PutBuffer(b) - - right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right - left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left - ch <- res{n, err} + defer wg.Done() + _, err1 = Copy(right, left) + right.SetReadDeadline(time.Now().Add(wait)) // unblock read on right }() - b := pool.GetBuffer(TCPBufSize) - n, err := io.CopyBuffer(left, right, b) - pool.PutBuffer(b) + _, err = Copy(left, right) + left.SetReadDeadline(time.Now().Add(wait)) // unblock read on left + wg.Wait() - right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right - left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left - rs := <-ch - - if err == nil { - err = rs.Err + if err1 != nil && !errors.Is(err1, os.ErrDeadlineExceeded) { // requires Go 1.15+ + return err1 } - return n, rs.N, err + + if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) { + return err + } + + return nil +} + +// Copy copies from src to dst. +func Copy(dst io.Writer, src io.Reader) (written int64, err error) { + buf := pool.GetBuffer(TCPBufSize) + defer pool.PutBuffer(buf) + + return io.CopyBuffer(dst, src, buf) } // RelayUDP copys from src to dst at target with read timeout. diff --git a/dns/server.go b/dns/server.go index d6c1556..6acf529 100644 --- a/dns/server.go +++ b/dns/server.go @@ -49,20 +49,20 @@ func (s *Server) Start() { // ListenAndServeUDP listen and serves on udp port. func (s *Server) ListenAndServeUDP(wg *sync.WaitGroup) { - c, err := net.ListenPacket("udp", s.addr) + pc, err := net.ListenPacket("udp", s.addr) wg.Done() if err != nil { log.F("[dns] failed to listen on %s, error: %v", s.addr, err) return } - defer c.Close() + defer pc.Close() log.F("[dns] listening UDP on %s", s.addr) for { reqBytes := pool.GetBuffer(UDPMaxLen) - n, caddr, err := c.ReadFrom(reqBytes[2:]) + n, caddr, err := pc.ReadFrom(reqBytes[2:]) if err != nil { log.F("[dns] local read error: %v", err) pool.PutBuffer(reqBytes) @@ -77,27 +77,28 @@ func (s *Server) ListenAndServeUDP(wg *sync.WaitGroup) { } binary.BigEndian.PutUint16(reqBytes[:2], reqLen) - go func() { - respBytes, err := s.Exchange(reqBytes[:2+n], caddr.String(), false) - defer func() { - pool.PutBuffer(reqBytes) - pool.PutBuffer(respBytes) - }() + go s.ServePacket(pc, caddr, reqBytes[:2+n]) + } +} - if err != nil { - log.F("[dns] error in exchange: %s", err) - return - } +// ServePacket serves dns packet conn. +func (s *Server) ServePacket(pc net.PacketConn, caddr net.Addr, reqBytes []byte) { + respBytes, err := s.Exchange(reqBytes, caddr.String(), false) + defer func() { + pool.PutBuffer(reqBytes) + pool.PutBuffer(respBytes) + }() - _, err = c.WriteTo(respBytes[2:], caddr) - if err != nil { - log.F("[dns] error in local write: %s", err) - return - } - - }() + if err != nil { + log.F("[dns] error in exchange: %s", err) + return } + _, err = pc.WriteTo(respBytes[2:], caddr) + if err != nil { + log.F("[dns] error in local write: %s", err) + return + } } // ListenAndServeTCP listen and serves on tcp port. @@ -122,7 +123,7 @@ func (s *Server) ListenAndServeTCP(wg *sync.WaitGroup) { } } -// ServeTCP serves a tcp connection. +// ServeTCP serves a dns tcp connection. func (s *Server) ServeTCP(c net.Conn) { defer c.Close() diff --git a/go.mod b/go.mod index f0eb814..58250fb 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/xtaci/kcp-go/v5 v5.5.15 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect - golang.org/x/sys v0.0.0-20200821140526-fda516888d29 // indirect - golang.org/x/tools v0.0.0-20200822203824-307de81be3f4 // indirect + golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 // indirect + golang.org/x/tools v0.0.0-20200823205832-c024452afbcd // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) diff --git a/go.sum b/go.sum index 112f3cf..693dbf1 100644 --- a/go.sum +++ b/go.sum @@ -117,15 +117,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200821140526-fda516888d29 h1:mNuhGagCf3lDDm5C0376C/sxh6V7fy9WbdEu/YDNA04= -golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 h1:AvbQYmiaaaza3cW3QXRyPo5kYgpFIzOAfeAAN7m3qQ4= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200822203824-307de81be3f4 h1:r0nbB2EeRbGpnVeqxlkgiBpNi/bednpSg78qzZGOuv0= -golang.org/x/tools v0.0.0-20200822203824-307de81be3f4/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200823205832-c024452afbcd h1:KNSumuk5eGuQV7zbOrDDZ3MIkwsQr0n5oKiH4oE0/hU= +golang.org/x/tools v0.0.0-20200823205832-c024452afbcd/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/proxy/http/server.go b/proxy/http/server.go index 1fd9ce9..0b2f703 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -102,11 +102,7 @@ func (s *HTTP) servHTTPS(r *request, c net.Conn) { log.F("[http] %s <-> %s [c] via %s", c.RemoteAddr(), r.uri, dialer.Addr()) - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } + if err = conn.Relay(c, rc); err != nil { log.F("[http] relay error: %v", err) s.proxy.Record(dialer, false) } diff --git a/proxy/redir/redir_linux.go b/proxy/redir/redir_linux.go index 227bf99..8dc0aea 100644 --- a/proxy/redir/redir_linux.go +++ b/proxy/redir/redir_linux.go @@ -113,11 +113,7 @@ func (s *RedirProxy) Serve(c net.Conn) { log.F("[redir] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } + if err = conn.Relay(c, rc); err != nil { log.F("[redir] relay error: %v", err) s.proxy.Record(dialer, false) } diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 5354438..a018d45 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -141,11 +141,7 @@ func (s *Socks5) Serve(c net.Conn) { log.F("[socks5] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } + if err = conn.Relay(c, rc); err != nil { log.F("[socks5] relay error: %v", err) s.proxy.Record(dialer, false) } diff --git a/proxy/ss/ss.go b/proxy/ss/ss.go index 9f8a209..a6694f7 100644 --- a/proxy/ss/ss.go +++ b/proxy/ss/ss.go @@ -160,11 +160,7 @@ func (s *SS) Serve(c net.Conn) { log.F("[ss] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } + if err = conn.Relay(c, rc); err != nil { log.F("[ss] relay error: %v", err) s.proxy.Record(dialer, false) } diff --git a/proxy/tcptun/tcptun.go b/proxy/tcptun/tcptun.go index 3c87a64..4ade827 100644 --- a/proxy/tcptun/tcptun.go +++ b/proxy/tcptun/tcptun.go @@ -90,12 +90,8 @@ func (s *TCPTun) Serve(c net.Conn) { log.F("[tcptun] %s <-> %s via %s", c.RemoteAddr(), s.raddr, dialer.Addr()) - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("relay error: %v", err) + if err = conn.Relay(c, rc); err != nil { + log.F("[tcptun] relay error: %v", err) s.proxy.Record(dialer, false) } } diff --git a/proxy/udptun/udptun.go b/proxy/udptun/udptun.go index 0bee32a..9aef394 100644 --- a/proxy/udptun/udptun.go +++ b/proxy/udptun/udptun.go @@ -87,11 +87,11 @@ func (s *UDPTun) ListenAndServe() { nm.Store(raddr.String(), pc) - go func() { + go func(c, pc net.PacketConn, raddr net.Addr) { conn.RelayUDP(c, raddr, pc, 2*time.Minute) pc.Close() nm.Delete(raddr.String()) - }() + }(c, pc, raddr) } else { pc = v.(net.PacketConn) diff --git a/proxy/vmess/chunk.go b/proxy/vmess/chunk.go index 828869b..0f6ec7a 100644 --- a/proxy/vmess/chunk.go +++ b/proxy/vmess/chunk.go @@ -25,8 +25,7 @@ func (w *chunkedWriter) Write(b []byte) (n int, err error) { buf := pool.GetBuffer(lenSize + chunkSize) defer pool.PutBuffer(buf) - left := len(b) - for left != 0 { + for left := len(b); left != 0; { writeLen := left if writeLen > chunkSize { writeLen = chunkSize @@ -49,6 +48,7 @@ func (w *chunkedWriter) Write(b []byte) (n int, err error) { type chunkedReader struct { io.Reader + buf [lenSize]byte left int } @@ -60,13 +60,11 @@ func ChunkedReader(r io.Reader) io.Reader { func (r *chunkedReader) Read(b []byte) (int, error) { if r.left == 0 { // get length - buf := pool.GetBuffer(lenSize) - _, err := io.ReadFull(r.Reader, buf[:lenSize]) + _, err := io.ReadFull(r.Reader, r.buf[:lenSize]) if err != nil { return 0, err } - r.left = int(binary.BigEndian.Uint16(buf[:lenSize])) - pool.PutBuffer(buf) + r.left = int(binary.BigEndian.Uint16(r.buf[:lenSize])) // if left == 0, then this is the end if r.left == 0 { diff --git a/proxy/ws/client.go b/proxy/ws/client.go index cdb5013..3fe861b 100644 --- a/proxy/ws/client.go +++ b/proxy/ws/client.go @@ -93,7 +93,6 @@ func (c *Conn) Write(b []byte) (n int, err error) { if c.writer == nil { c.writer = FrameWriter(c.Conn) } - return c.writer.Write(b) } @@ -101,7 +100,6 @@ func (c *Conn) Read(b []byte) (n int, err error) { if c.reader == nil { c.reader = FrameReader(c.Conn) } - return c.reader.Read(b) } diff --git a/proxy/ws/frame.go b/proxy/ws/frame.go index 7682cf8..e28cbc0 100644 --- a/proxy/ws/frame.go +++ b/proxy/ws/frame.go @@ -126,20 +126,17 @@ func (w *frameWriter) ReadFrom(r io.Reader) (n int64, err error) { type frameReader struct { io.Reader - buf []byte - leftBytes int64 + buf [8]byte + left int64 } // FrameReader returns a chunked reader. func FrameReader(r io.Reader) io.Reader { - return &frameReader{ - Reader: r, - buf: make([]byte, maxFrameHeaderSize), // NOTE: buf only used to save header bytes now - } + return &frameReader{Reader: r} } func (r *frameReader) Read(b []byte) (int, error) { - if r.leftBytes == 0 { + if r.left == 0 { // get msg header _, err := io.ReadFull(r.Reader, r.buf[:2]) if err != nil { @@ -149,26 +146,26 @@ func (r *frameReader) Read(b []byte) (int, error) { // final := r.buf[0]&finalBit != 0 // frameType := int(r.buf[0] & 0xf) // mask := r.buf[1]&maskBit != 0 - r.leftBytes = int64(r.buf[1] & 0x7f) - switch r.leftBytes { + r.left = int64(r.buf[1] & 0x7f) + switch r.left { case 126: _, err := io.ReadFull(r.Reader, r.buf[:2]) if err != nil { return 0, err } - r.leftBytes = int64(binary.BigEndian.Uint16(r.buf[0:])) + r.left = int64(binary.BigEndian.Uint16(r.buf[:2])) case 127: _, err := io.ReadFull(r.Reader, r.buf[:8]) if err != nil { return 0, err } - r.leftBytes = int64(binary.BigEndian.Uint64(r.buf[0:])) + r.left = int64(binary.BigEndian.Uint64(r.buf[:8])) } } readLen := int64(len(b)) - if readLen > r.leftBytes { - readLen = r.leftBytes + if readLen > r.left { + readLen = r.left } m, err := r.Reader.Read(b[:readLen]) @@ -176,6 +173,6 @@ func (r *frameReader) Read(b []byte) (int, error) { return 0, err } - r.leftBytes -= int64(m) + r.left -= int64(m) return m, err }