From a1ff92201cef9caf8adebb6440874dba1727ac18 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sun, 11 Oct 2020 19:53:38 +0800 Subject: [PATCH] trojan: support fallback --- README.md | 4 +- config.go | 4 +- config/glider.conf.example | 8 +- proxy/trojan/client.go | 4 +- proxy/trojan/server.go | 51 +++++++++--- proxy/trojan/trojan.go | 27 ++++--- proxy/vless/server.go | 154 +++++++++++++++++++------------------ 7 files changed, 149 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index b10fd58..a006b6c 100644 --- a/README.md +++ b/README.md @@ -177,8 +177,8 @@ Trojan client scheme: trojanc://pass@host:port (cleartext, without TLS) Trojan server scheme: - trojan://pass@host:port?cert=PATH&key=PATH - trojanc://pass@host:port (cleartext, without TLS) + trojan://pass@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1] + trojanc://pass@host:port[?fallback=127.0.0.1] (cleartext, without TLS) Available securities for vmess: none, aes-128-gcm, chacha20-poly1305 diff --git a/config.go b/config.go index b78b143..b4c991b 100644 --- a/config.go +++ b/config.go @@ -174,8 +174,8 @@ func usage() { fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Trojan server scheme:\n") - fmt.Fprintf(w, " trojan://pass@host:port?cert=PATH&key=PATH\n") - fmt.Fprintf(w, " trojanc://pass@host:port (cleartext, without TLS)\n") + fmt.Fprintf(w, " trojan://pass@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1]\n") + fmt.Fprintf(w, " trojanc://pass@host:port[?fallback=127.0.0.1] (cleartext, without TLS)\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Available securities for vmess:\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index 1f6085e..42cd03d 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -79,7 +79,10 @@ listen=socks5://:1080 # listen=tls://:1234?cert=/path/to/cert&key=/path/to/key,vless://UUID@?fallback=127.0.0.1:80 # trojan server -# listen=trojan://PASSWORD:1234?cert=/path/to/cert&key=/path/to/key +# listen=trojan://PASSWORD:1234?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1 + +# trojanc server (trojan without tls) +# listen=trojanc://PASSWORD:1234?fallback=127.0.0.1 # FORWARDERS # ---------- @@ -119,6 +122,9 @@ listen=socks5://:1080 # trojan as forwarder # forward=trojan://PASSWORD@1.1.1.1:8080[?serverName=SERVERNAME][&skipVerify=true] +# trojanc as forwarder +# forward=trojanc://PASSWORD@1.1.1.1:8080 + # vless forwarder # forward=vless://5a146038-0b56-4e95-b1dc-5c6f5a32cd98@1.1.1.1:443 diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go index 82841ed..9c5650e 100644 --- a/proxy/trojan/client.go +++ b/proxy/trojan/client.go @@ -18,7 +18,7 @@ func NewClearTextDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { return nil, err } - t.clearText = true + t.withTLS = false return t, err } @@ -61,7 +61,7 @@ func (s *Trojan) dial(network, addr string) (net.Conn, error) { return nil, err } - if !s.clearText { + if s.withTLS { tlsConn := tls.Client(rc, s.tlsConfig) if err := tlsConn.Handshake(); err != nil { return nil, err diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index b80faf6..724ef14 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -23,7 +23,7 @@ func NewClearTextServer(s string, p proxy.Proxy) (proxy.Server, error) { return nil, err } - t.clearText = true + t.withTLS = false return t, nil } @@ -35,6 +35,10 @@ func NewTrojanServer(s string, p proxy.Proxy) (proxy.Server, error) { return nil, err } + if t.certFile == "" || t.keyFile == "" { + return nil, errors.New("[trojan] cert and key file path must be spcified") + } + cert, err := tls.LoadX509KeyPair(t.certFile, t.keyFile) if err != nil { log.F("[trojan] unable to load cert: %s, key %s", t.certFile, t.keyFile) @@ -58,7 +62,7 @@ func (s *Trojan) ListenAndServe() { } defer l.Close() - log.F("[trojan] listening TCP on %s with TLS", s.addr) + log.F("[trojan] listening TCP on %s, with TLS: %v", s.addr, s.withTLS) for { c, err := l.Accept() @@ -79,7 +83,7 @@ func (s *Trojan) Serve(c net.Conn) { c.SetKeepAlive(true) } - if !s.clearText { + if s.withTLS { tlsConn := tls.Server(c, s.tlsConfig) err := tlsConn.Handshake() if err != nil { @@ -89,9 +93,15 @@ func (s *Trojan) Serve(c net.Conn) { c = tlsConn } - cmd, target, err := s.readHeader(c) + headBuf := pool.GetWriteBuffer() + defer pool.PutWriteBuffer(headBuf) + + cmd, target, err := s.readHeader(io.TeeReader(c, headBuf)) if err != nil { - log.F("[trojan] error in server handshake: %s", err) + log.F("[trojan] verify header from %s error: %v", c.RemoteAddr(), err) + if s.fallback != "" { + s.serveFallback(c, s.fallback, headBuf) + } return } @@ -125,12 +135,34 @@ func (s *Trojan) Serve(c net.Conn) { } } -func (s *Trojan) readHeader(c net.Conn) (byte, socks.Addr, error) { +func (s *Trojan) serveFallback(c net.Conn, tgt string, headBuf *bytes.Buffer) { + dialer := s.proxy.NextDialer(tgt) + rc, err := dialer.Dial("tcp", tgt) + if err != nil { + log.F("[trojan-fallback] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + return + } + defer rc.Close() + + _, err = rc.Write(headBuf.Bytes()) + if err != nil { + log.F("[trojan-fallback] write to rc error: %v", err) + return + } + + log.F("[trojan-fallback] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[trojan-fallback] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + } +} + +func (s *Trojan) readHeader(r io.Reader) (byte, socks.Addr, error) { // pass: 56, "\r\n": 2, cmd: 1 buf := pool.GetBuffer(59) defer pool.PutBuffer(buf) - if _, err := io.ReadFull(c, buf); err != nil { + if _, err := io.ReadFull(r, buf); err != nil { return socks.CmdError, nil, err } @@ -143,13 +175,13 @@ func (s *Trojan) readHeader(c net.Conn) (byte, socks.Addr, error) { cmd := byte(buf[58]) // target - tgt, err := socks.ReadAddr(c) + tgt, err := socks.ReadAddr(r) if err != nil { return cmd, nil, fmt.Errorf("read target address error: %v", err) } // "\r\n", 2bytes - if _, err := io.ReadFull(c, buf[:2]); err != nil { + if _, err := io.ReadFull(r, buf[:2]); err != nil { return socks.CmdError, tgt, err } @@ -203,6 +235,7 @@ func (s *Trojan) ServeUoT(c net.Conn, tgt socks.Addr) { break } + // WriteTo addr can be nil because the PktConn has it's own target, see packet.go _, err = pc.WriteTo(buf[:n], nil) if err != nil { log.F("[trojan] write pc error: %v", err) diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index 9236a63..dfb8531 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -18,20 +18,17 @@ import ( // Trojan is a base trojan struct. type Trojan struct { - dialer proxy.Dialer - proxy proxy.Proxy - addr string - pass [56]byte - - clearText bool - - tlsConfig *tls.Config - + dialer proxy.Dialer + proxy proxy.Proxy + addr string + pass [56]byte + withTLS bool + tlsConfig *tls.Config serverName string skipVerify bool - - certFile string - keyFile string + certFile string + keyFile string + fallback string } func init() { @@ -53,10 +50,12 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { dialer: d, proxy: p, addr: u.Host, + withTLS: true, skipVerify: query.Get("skipVerify") == "true", serverName: query.Get("serverName"), certFile: query.Get("cert"), keyFile: query.Get("key"), + // fallback: "127.0.0.1:80", } if t.serverName == "" { @@ -78,5 +77,9 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { hash.Write([]byte(pass)) hex.Encode(t.pass[:], hash.Sum(nil)) + if fb := u.Query().Get("fallback"); fb != "" { + t.fallback = fb + } + return t, nil } diff --git a/proxy/vless/server.go b/proxy/vless/server.go index 02112b4..4d2e612 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -49,41 +49,28 @@ func (s *VLess) Serve(c net.Conn) { c.SetKeepAlive(true) } - var fallback bool - target := s.fallback + headBuf := pool.GetWriteBuffer() + defer pool.PutWriteBuffer(headBuf) - wbuf := pool.GetWriteBuffer() - defer pool.PutWriteBuffer(wbuf) - - cmd, err := s.readHeader(io.TeeReader(c, wbuf)) + cmd, target, err := s.readHeader(io.TeeReader(c, headBuf)) if err != nil { - if s.fallback == "" { - log.F("[vless] verify header from %s error: %v", c.RemoteAddr(), err) - return + log.F("[vless] verify header from %s error: %v", c.RemoteAddr(), err) + if s.fallback != "" { + s.serveFallback(c, s.fallback, headBuf) } - fallback = true - log.F("[vless] verify header from %s error: %v, fallback to %s", c.RemoteAddr(), err, s.fallback) + return } network := "tcp" 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) + + if cmd == CmdUDP { + // there is no upstream proxy, just serve it + if dialer.Addr() == "DIRECT" { + s.ServeUoT(c, target) return } - dialer = s.proxy.NextDialer(target) - - if cmd == CmdUDP { - // there is no upstream proxy, just serve it - if dialer.Addr() == "DIRECT" { - s.ServeUoT(c, target) - return - } - network = "udp" - } + network = "udp" } rc, err := dialer.Dial(network, target) @@ -93,17 +80,9 @@ func (s *VLess) Serve(c net.Conn) { } defer rc.Close() - 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 { + if err = proxy.Relay(NewServerConn(c), rc); err != nil { 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) { @@ -112,6 +91,71 @@ func (s *VLess) Serve(c net.Conn) { } } +func (s *VLess) serveFallback(c net.Conn, tgt string, headBuf *bytes.Buffer) { + dialer := s.proxy.NextDialer(tgt) + rc, err := dialer.Dial("tcp", tgt) + if err != nil { + log.F("[vless-fallback] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + return + } + defer rc.Close() + + _, err = rc.Write(headBuf.Bytes()) + if err != nil { + log.F("[vless-fallback] write to rc error: %v", err) + return + } + + log.F("[vless-fallback] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[vless-fallback] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + } +} + +func (s *VLess) readHeader(r io.Reader) (CmdType, string, error) { + buf := pool.GetBuffer(16) + defer pool.PutBuffer(buf) + + // ver + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, "", fmt.Errorf("get version error: %v", err) + } + + if buf[0] != Version { + return CmdErr, "", fmt.Errorf("version %d not supported", buf[0]) + } + + // uuid + if _, err := io.ReadFull(r, buf[:16]); err != nil { + return CmdErr, "", fmt.Errorf("get uuid error: %v", err) + } + + if !bytes.Equal(s.uuid[:], buf) { + return CmdErr, "", fmt.Errorf("auth failed, client id: %02x", buf[:16]) + } + + // addLen + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, "", fmt.Errorf("get addon length error: %v", err) + } + + // ignore addons + if addLen := int64(buf[0]); addLen > 0 { + proxy.CopyN(ioutil.Discard, r, addLen) + } + + // cmd + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, "", fmt.Errorf("get cmd error: %v", err) + } + + // target + target, err := ReadAddrString(r) + + return CmdType(buf[0]), target, err +} + // ServeUoT serves udp over tcp requests. func (s *VLess) ServeUoT(c net.Conn, tgt string) { rc, err := net.ListenPacket("udp", "") @@ -174,46 +218,6 @@ func (s *VLess) ServeUoT(c net.Conn, tgt string) { } -func (s *VLess) readHeader(r io.Reader) (CmdType, error) { - buf := pool.GetBuffer(16) - defer pool.PutBuffer(buf) - - // ver - if _, err := io.ReadFull(r, buf[:1]); err != nil { - return CmdErr, fmt.Errorf("get version error: %v", err) - } - - if buf[0] != Version { - return CmdErr, fmt.Errorf("version %d not supported", buf[0]) - } - - // uuid - if _, err := io.ReadFull(r, buf[:16]); err != nil { - return CmdErr, fmt.Errorf("get uuid error: %v", err) - } - - if !bytes.Equal(s.uuid[:], buf) { - return CmdErr, fmt.Errorf("auth failed, client id: %02x", buf[:16]) - } - - // addLen - if _, err := io.ReadFull(r, buf[:1]); err != nil { - return CmdErr, fmt.Errorf("get addon length error: %v", err) - } - - // ignore addons - if addLen := int64(buf[0]); addLen > 0 { - proxy.CopyN(ioutil.Discard, r, addLen) - } - - // cmd - if _, err := io.ReadFull(r, buf[:1]); err != nil { - return CmdErr, fmt.Errorf("get cmd error: %v", err) - } - - return CmdType(buf[0]), nil -} - // ServerConn is a vless client connection. type ServerConn struct { net.Conn