From 829a0d7f80b6fd325ab2a23803ff40c0304c861c Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sun, 4 Oct 2020 18:26:44 +0800 Subject: [PATCH] vless: support fallback to a http server --- .github/workflows/docker.yml | 20 ----------- README.md | 2 +- config.go | 2 +- config/glider.conf.example | 2 ++ proxy/vless/server.go | 67 +++++++++++++++++++++++++----------- proxy/vless/vless.go | 14 +++++--- 6 files changed, 60 insertions(+), 47 deletions(-) delete mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 6045211..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Publish Docker image -on: - release: - types: [published] - -jobs: - push_to_registry: - name: Push Docker image to Docker Hub - runs-on: ubuntu-latest - steps: - - name: Check out the repo - uses: actions/checkout@v2 - - name: Push to Docker Hub - uses: docker/build-push-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - repository: nadoo/glider - tag_with_ref: true - \ No newline at end of file diff --git a/README.md b/README.md index 7498a75..c925688 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ VMess scheme: vmess://[security:]uuid@host:port?alterID=num VLESS scheme: - vless://uuid@host:port + vless://uuid@host:port[?fallback=127.0.0.1:80] Trojan scheme: trojan://pass@host:port[?skipVerify=true] diff --git a/config.go b/config.go index 24e0449..7352081 100644 --- a/config.go +++ b/config.go @@ -162,7 +162,7 @@ func usage() { fmt.Fprintf(w, "\n") fmt.Fprintf(w, "VLESS scheme:\n") - fmt.Fprintf(w, " vless://uuid@host:port\n") + fmt.Fprintf(w, " vless://uuid@host:port[?fallback=127.0.0.1:80]\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Trojan scheme:\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index 1c55e60..2bcfd0e 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -45,6 +45,8 @@ listen=socks5://:1080 # listen on 1234 as vless proxy server. # listen=vless://uuid@:1234 +# listen on 1234 as vless proxy server, fallback to 127.0.0.1:8080 http server when client auth failed. +# listen=vless://uuid@:1234?fallback=127.0.0.1:8080 # listen on 1081 as a linux transparent proxy server. # listen=redir://:1081 diff --git a/proxy/vless/server.go b/proxy/vless/server.go index 4612d2d..1af1407 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -49,41 +49,62 @@ func (s *VLess) Serve(c net.Conn) { c.SetKeepAlive(true) } - c = NewServerConn(c) - cmd, err := s.readHeader(c) - if err != nil { - log.F("[vless] verify header error: %v", err) - return - } + var fallback bool + var dialer proxy.Dialer + target := s.fallback - tgt, err := ReadAddrString(c) + wbuf := pool.GetWriteBuffer() + defer pool.PutWriteBuffer(wbuf) + + cmd, err := s.readHeader(io.TeeReader(c, wbuf)) if err != nil { - log.F("[vless] get target error: %v", err) - return + if s.fallback == "" { + log.F("[vless] verify header error: %v", err) + return + } + fallback = true + log.F("[vless] verify header error: %v, fallback to %s", err, s.fallback) } network := "tcp" - dialer := s.proxy.NextDialer(tgt) - if cmd == CmdUDP { - // there is no upstream proxy, just serve it - if dialer.Addr() == "DIRECT" { - s.ServeUoT(c, tgt) + dialer = s.proxy.NextDialer(target) + if !fallback { + c = NewServerConn(c) + target, err = ReadAddrString(c) + if err != nil { + log.F("[vless] get target error: %v", err) return } - network = "udp" + + if cmd == CmdUDP { + // there is no upstream proxy, just serve it + if dialer.Addr() == "DIRECT" { + s.ServeUoT(c, target) + return + } + network = "udp" + } } - rc, err := dialer.Dial(network, tgt) + rc, err := dialer.Dial(network, target) if err != nil { - log.F("[vless] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + log.F("[vless] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), target, dialer.Addr(), err) return } defer rc.Close() - log.F("[vless] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + if fallback { + _, err := rc.Write(wbuf.Bytes()) + if err != nil { + log.F("[vless] write to rc error: %v", err) + return + } + } + + log.F("[vless] %s <-> %s via %s", c.RemoteAddr(), target, dialer.Addr()) if err = proxy.Relay(c, rc); err != nil { - log.F("[vless] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + log.F("[vless] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), target, dialer.Addr(), err) // record remote conn failure only if !strings.Contains(err.Error(), s.addr) { s.proxy.Record(dialer, false) @@ -91,7 +112,7 @@ func (s *VLess) Serve(c net.Conn) { } } -// ServeUOT serves udp over tcp requests. +// ServeUoT serves udp over tcp requests. func (s *VLess) ServeUoT(c net.Conn, tgt string) { rc, err := net.ListenPacket("udp", "") if err != nil { @@ -118,6 +139,10 @@ func (s *VLess) ServeUoT(c net.Conn, tgt string) { length := binary.BigEndian.Uint16(buf[:2]) n, err := io.ReadFull(c, buf[:length]) + if err != nil { + log.F("[vless] read payload error: %s\n", err) + return + } _, err = rc.WriteTo(buf[:n], tgtAddr) if err != nil { @@ -127,7 +152,7 @@ func (s *VLess) ServeUoT(c net.Conn, tgt string) { } }() - log.F("[vless] %s <-tcp-> %s - %s <-udp-> %s via DIRECT", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) + log.F("[vless] %s <-tcp-> %s - %s <-udp-> %s", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) buf := pool.GetBuffer(proxy.UDPBufSize) defer pool.PutBuffer(buf) diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index a02b91b..288698c 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -24,10 +24,11 @@ const ( // VLess struct. type VLess struct { - dialer proxy.Dialer - proxy proxy.Proxy - addr string - uuid [16]byte + dialer proxy.Dialer + proxy proxy.Proxy + addr string + uuid [16]byte + fallback string } func init() { @@ -55,6 +56,11 @@ func NewVLess(s string, d proxy.Dialer, p proxy.Proxy) (*VLess, error) { uuid: uuid, } + v.fallback = "127.0.0.1:80" + if custom := u.Query().Get("fallback"); custom != "" { + v.fallback = custom + } + return v, nil }