From 175ef16a5cdda8dde10e43329ef2d9176c7c8ec2 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sat, 10 Oct 2020 19:04:33 +0800 Subject: [PATCH] trojan: support listen as trojan server --- README.md | 13 ++- config.go | 8 +- config/glider.conf.example | 12 +++ dns/cache.go | 2 +- dns/client.go | 11 ++- dns/server.go | 11 ++- go.mod | 4 +- go.sum | 8 +- proxy/socks/socks.go | 1 + proxy/socks5/packet.go | 7 +- proxy/ss/packet.go | 7 +- proxy/tls/tls.go | 3 +- proxy/trojan/client.go | 86 ++++++++++++++++ proxy/trojan/server.go | 197 +++++++++++++++++++++++++++++++++++++ proxy/trojan/trojan.go | 92 +++-------------- proxy/vless/packet.go | 6 +- proxy/vless/server.go | 4 +- 17 files changed, 357 insertions(+), 115 deletions(-) create mode 100644 proxy/trojan/client.go create mode 100644 proxy/trojan/server.go diff --git a/README.md b/README.md index 6cc78cd..e84d40a 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ we can set up local listeners as proxy servers, and forward requests to internet |ss |√|√|√|√|client & server |ssr | | |√| |client only |ssh | | |√| |client only -|trojan | | |√|√|client only +|trojan |√|√|√|√|client & server |vless |√|√|√|√|client & server |vmess | | |√| |client only |redir |√| | | |linux only @@ -88,7 +88,7 @@ glider -h click to see details ```bash -./glider 0.11.2 usage: +./glider 0.12.0 usage: -checkdisabledonly check disabled fowarders only -checkinterval int @@ -106,7 +106,7 @@ glider -h -dnsalwaystcp always use tcp to query upstream dns servers no matter there is a forwarder or not -dnscachesize int - size of CACHE (default 1024) + size of CACHE (default 4096) -dnsmaxttl int maximum TTL value for entries in the CACHE(seconds) (default 1800) -dnsminttl int @@ -141,7 +141,7 @@ glider -h verbose mode Available schemes: - listen: mixed ss socks5 http vless redir redir6 tcptun udptun uottun tls unix kcp + listen: mixed ss socks5 http vless trojan redir redir6 tcptun udptun uottun tls unix kcp forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs Socks5 scheme: @@ -171,9 +171,12 @@ VMess scheme: VLESS scheme: vless://uuid@host:port[?fallback=127.0.0.1:80] -Trojan scheme: +Trojan client scheme: trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true] +Trojan server scheme: + trojan://pass@host:port?cert=PATH&key=PATH + Available securities for vmess: none, aes-128-gcm, chacha20-poly1305 diff --git a/config.go b/config.go index e10e968..6d9f739 100644 --- a/config.go +++ b/config.go @@ -130,7 +130,7 @@ func usage() { fmt.Fprintf(w, "\n") fmt.Fprintf(w, "Available schemes:\n") - fmt.Fprintf(w, " listen: mixed ss socks5 http vless redir redir6 tcptun udptun uottun tls unix kcp\n") + fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan redir redir6 tcptun udptun uottun tls unix kcp\n") fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs\n") fmt.Fprintf(w, "\n") @@ -168,10 +168,14 @@ func usage() { fmt.Fprintf(w, " vless://uuid@host:port[?fallback=127.0.0.1:80]\n") fmt.Fprintf(w, "\n") - fmt.Fprintf(w, "Trojan scheme:\n") + fmt.Fprintf(w, "Trojan client scheme:\n") fmt.Fprintf(w, " trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true]\n") 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, "\n") + fmt.Fprintf(w, "Available securities for vmess:\n") fmt.Fprintf(w, " none, aes-128-gcm, chacha20-poly1305\n") fmt.Fprintf(w, "\n") diff --git a/config/glider.conf.example b/config/glider.conf.example index 839b25c..8db3e70 100644 --- a/config/glider.conf.example +++ b/config/glider.conf.example @@ -72,6 +72,15 @@ listen=socks5://:1080 # socks5 over kcp # listen=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,socks5:// +# vless server +# listen=vless://UUID@:1234 + +# vless over tls server +# 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 + # FORWARDERS # ---------- # Forwarders, we can setup multiple forwarders. @@ -216,6 +225,9 @@ dnsmaxttl=1800 # minimum TTL value for entries in the CACHE(seconds) dnsminttl=0 +# size of CACHE +dnscachesize=4096 + # custom records dnsrecord=www.example.com/1.2.3.4 dnsrecord=www.example.com/2606:2800:220:1:248:1893:25c8:1946 diff --git a/dns/cache.go b/dns/cache.go index 9fca040..1c7fc2b 100644 --- a/dns/cache.go +++ b/dns/cache.go @@ -83,7 +83,7 @@ func (c *LruCache) Set(k string, v []byte, ttl int) { c.putToHead(k, v, exp) - // NOTE: the cache size will always be c.size + 2, + // NOTE: the cache size will always >= 2, // but it doesn't matter in our environment. if len(c.cache) > c.size { c.removeTail() diff --git a/dns/client.go b/dns/client.go index 616c3b4..0316865 100644 --- a/dns/client.go +++ b/dns/client.go @@ -76,7 +76,7 @@ func (c *Client) Exchange(reqBytes []byte, clientAddr string, preferTCP bool) ([ go func(qname string, reqBytes []byte, preferTCP bool) { defer pool.PutBuffer(reqBytes) if dnsServer, network, dialerAddr, respBytes, err := c.exchange(qname, reqBytes, preferTCP); err == nil { - c.handleAnswer(respBytes, clientAddr, dnsServer, network, dialerAddr) + c.handleAnswer(respBytes, "cache", dnsServer, network, dialerAddr) } }(req.Question.QNAME, valCopy(reqBytes), preferTCP) } @@ -209,12 +209,13 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) ( // exchangeTCP exchange with server over tcp. func (c *Client) exchangeTCP(rc net.Conn, reqBytes []byte) ([]byte, error) { - reqLen := pool.GetBuffer(2) - defer pool.PutBuffer(reqLen) + buf := pool.GetBuffer(2 + len(reqBytes)) + defer pool.PutBuffer(buf) - binary.BigEndian.PutUint16(reqLen, uint16(len(reqBytes))) + binary.BigEndian.PutUint16(buf[:2], uint16(len(reqBytes))) + copy(buf[2:], reqBytes) - if _, err := (&net.Buffers{reqLen, reqBytes}).WriteTo(rc); err != nil { + if _, err := rc.Write(buf); err != nil { return nil, err } diff --git a/dns/server.go b/dns/server.go index 3cb6fe6..329e448 100644 --- a/dns/server.go +++ b/dns/server.go @@ -143,12 +143,15 @@ func (s *Server) ServeTCP(c net.Conn) { return } - respLen := pool.GetBuffer(2) - defer pool.PutBuffer(respLen) - binary.BigEndian.PutUint16(respLen, uint16(len(respBytes))) + buf := pool.GetBuffer(2 + len(respBytes)) + defer pool.PutBuffer(buf) - if _, err := (&net.Buffers{respLen, respBytes}).WriteTo(c); err != nil { + binary.BigEndian.PutUint16(buf[:2], uint16(len(respBytes))) + copy(buf[2:], respBytes) + + if _, err := c.Write(buf); err != nil { log.F("[dns-tcp] error in write respBytes: %s", err) return } + } diff --git a/go.mod b/go.mod index dced3ac..2229d99 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,11 @@ require ( github.com/nadoo/go-shadowsocks2 v0.1.2 github.com/nadoo/ipset v0.3.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/xtaci/kcp-go/v5 v5.5.17 + github.com/xtaci/kcp-go/v5 v5.6.1 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 golang.org/x/net v0.0.0-20201009032441-dbdefad45b89 // indirect golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect - golang.org/x/tools v0.0.0-20201009032223-96877f285f7e // indirect + golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 684dc79..ca8b5d3 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/xtaci/kcp-go v5.4.11+incompatible h1:tJbtarpmOoOD74cZ41uvvF5Hyt1nvctH github.com/xtaci/kcp-go v5.4.11+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/kcp-go/v5 v5.5.12 h1:iALGyvti/oBbl1TbVoUpHEUHCorDEb3tEKl1CPY3KXM= github.com/xtaci/kcp-go/v5 v5.5.12/go.mod h1:H0T/EJ+lPNytnFYsKLH0JHUtiwZjG3KXlTM6c+Q4YUo= -github.com/xtaci/kcp-go/v5 v5.5.17 h1:bkdaqtER0PMlP05BBHfu6W+71kt/NwbAk93KH7F78Ck= -github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0= +github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI= +github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -177,8 +177,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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-20201009032223-96877f285f7e h1:G1acLyqfyttmexrW7XPhzsaS8m6s+P9XsW9djwh10s4= -golang.org/x/tools v0.0.0-20201009032223-96877f285f7e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91 h1:UNUk0ao5UA0V4v2wikQWc4U+yG5UGoWku8MHs27mMqs= +golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 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/socks/socks.go b/proxy/socks/socks.go index bbab7f1..94d0655 100644 --- a/proxy/socks/socks.go +++ b/proxy/socks/socks.go @@ -15,6 +15,7 @@ const ( // SOCKS request commands as defined in RFC 1928 section 4 const ( + CmdError byte = 0 CmdConnect byte = 1 CmdBind byte = 2 CmdUDPAssociate byte = 3 diff --git a/proxy/socks5/packet.go b/proxy/socks5/packet.go index defb594..19e8169 100644 --- a/proxy/socks5/packet.go +++ b/proxy/socks5/packet.go @@ -73,11 +73,12 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { // +----+------+------+----------+----------+----------+ // | 2 | 1 | 1 | Variable | 2 | Variable | // +----+------+------+----------+----------+----------+ - tgtAddr := socks.SplitAddr(buf[3:]) + tgtAddr := socks.SplitAddr(buf[3:n]) if tgtAddr == nil { return n, raddr, errors.New("can not get addr") } - copy(b, buf[3+len(tgtAddr):]) + + n = copy(b, buf[3+len(tgtAddr):n]) //test if pc.writeAddr == nil { @@ -88,7 +89,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { pc.tgtAddr = tgtAddr } - return n - len(tgtAddr) - 3, raddr, err + return n, raddr, err } // WriteTo overrides the original function from net.PacketConn. diff --git a/proxy/ss/packet.go b/proxy/ss/packet.go index cb59369..9f66df3 100644 --- a/proxy/ss/packet.go +++ b/proxy/ss/packet.go @@ -42,11 +42,12 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { return n, raddr, err } - tgtAddr := socks.SplitAddr(buf) + tgtAddr := socks.SplitAddr(buf[:n]) if tgtAddr == nil { return n, raddr, errors.New("can not get addr") } - copy(b, buf[len(tgtAddr):]) + + n = copy(b, buf[len(tgtAddr):n]) //test if pc.writeAddr == nil { @@ -57,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { pc.tgtAddr = tgtAddr } - return n - len(tgtAddr), raddr, err + return n, raddr, err } // WriteTo overrides the original function from net.PacketConn diff --git a/proxy/tls/tls.go b/proxy/tls/tls.go index de5588e..29554ef 100644 --- a/proxy/tls/tls.go +++ b/proxy/tls/tls.go @@ -24,7 +24,6 @@ type TLS struct { certFile string keyFile string - cert stdtls.Certificate server proxy.Server } @@ -76,7 +75,7 @@ func NewTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { ServerName: p.serverName, InsecureSkipVerify: p.skipVerify, ClientSessionCache: stdtls.NewLRUClientSessionCache(64), - MinVersion: stdtls.VersionTLS10, + MinVersion: stdtls.VersionTLS12, } return p, err diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go new file mode 100644 index 0000000..9715192 --- /dev/null +++ b/proxy/trojan/client.go @@ -0,0 +1,86 @@ +package trojan + +import ( + "crypto/tls" + "net" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewTrojanDialer returns a trojan proxy dialer. +func NewTrojanDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { + t, err := NewTrojan(s, d, nil) + if err != nil { + log.F("[trojan] create instance error: %s", err) + return nil, err + } + + t.tlsConfig = &tls.Config{ + ServerName: t.serverName, + InsecureSkipVerify: t.skipVerify, + NextProtos: []string{"http/1.1"}, + ClientSessionCache: tls.NewLRUClientSessionCache(64), + MinVersion: tls.VersionTLS12, + } + + return t, err +} + +// Addr returns forwarder's address. +func (s *Trojan) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// Dial connects to the address addr on the network net via the proxy. +func (s *Trojan) Dial(network, addr string) (net.Conn, error) { + return s.dial(network, addr) +} + +func (s *Trojan) dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + log.F("[trojan]: dial to %s error: %s", s.addr, err) + return nil, err + } + + tlsConn := tls.Client(rc, s.tlsConfig) + if err := tlsConn.Handshake(); err != nil { + return nil, err + } + + buf := pool.GetWriteBuffer() + defer pool.PutWriteBuffer(buf) + + buf.Write(s.pass[:]) + buf.WriteString("\r\n") + + cmd := socks.CmdConnect + if network == "udp" { + cmd = socks.CmdUDPAssociate + } + buf.WriteByte(cmd) + + buf.Write(socks.ParseAddr(addr)) + buf.WriteString("\r\n") + _, err = tlsConn.Write(buf.Bytes()) + + return tlsConn, err +} + +// DialUDP connects to the given address via the proxy. +func (s *Trojan) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { + c, err := s.dial("udp", addr) + if err != nil { + return nil, nil, err + } + + pkc := NewPktConn(c, socks.ParseAddr(addr)) + // TODO: check the addr in return value + return pkc, nil, nil +} diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go new file mode 100644 index 0000000..b1ebf27 --- /dev/null +++ b/proxy/trojan/server.go @@ -0,0 +1,197 @@ +package trojan + +import ( + "bytes" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "strings" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/socks" +) + +// NewTrojanServer returns a trojan proxy server. +func NewTrojanServer(s string, p proxy.Proxy) (proxy.Server, error) { + t, err := NewTrojan(s, nil, p) + if err != nil { + log.F("[trojan] create instance error: %s", err) + return nil, err + } + + 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) + return nil, err + } + + t.tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + } + + return t, err +} + +// ListenAndServe listen and serves connections. +func (s *Trojan) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("[trojan] failed to listen on %s: %v", s.addr, err) + return + } + defer l.Close() + + log.F("[trojan] listening TCP on %s with TLS", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[trojan] failed to accept: %v", err) + continue + } + + go s.Serve(c) + } +} + +// Serve serves a connection. +func (s *Trojan) Serve(cc net.Conn) { + defer cc.Close() + + if cc, ok := cc.(*net.TCPConn); ok { + cc.SetKeepAlive(true) + } + + c := tls.Server(cc, s.tlsConfig) + err := c.Handshake() + if err != nil { + log.F("[trojan] error in tls handshake: %s", err) + return + } + + cmd, target, err := s.readHeader(c) + if err != nil { + log.F("[trojan] error in server handshake: %s", err) + return + } + + network := "tcp" + dialer := s.proxy.NextDialer(target.String()) + + if cmd == socks.CmdUDPAssociate { + // there is no upstream proxy, just serve it + if dialer.Addr() == "DIRECT" { + s.ServeUoT(c, target) + return + } + network = "udp" + } + + rc, err := dialer.Dial(network, target.String()) + if err != nil { + log.F("[trojan] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), target, dialer.Addr(), err) + return + } + defer rc.Close() + + log.F("[trojan] %s <-> %s via %s", c.RemoteAddr(), target, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[trojan] %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) + } + } +} + +func (s *Trojan) readHeader(c net.Conn) (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 { + return socks.CmdError, nil, err + } + + // pass, 56bytes + if !bytes.Equal(buf[:56], s.pass[:]) { + return socks.CmdError, nil, errors.New("wrong password") + } + + // cmd, 1byte + cmd := byte(buf[58]) + + // target + tgt, err := socks.ReadAddr(c) + 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 { + return socks.CmdError, tgt, err + } + + return cmd, tgt, nil +} + +// ServeUoT serves udp over tcp requests. +func (s *Trojan) ServeUoT(c net.Conn, tgt socks.Addr) { + rc, err := net.ListenPacket("udp", "") + if err != nil { + log.F("[trojan] UDP remote listen error: %v", err) + return + } + defer rc.Close() + + tgtAddr, err := net.ResolveUDPAddr("udp", tgt.String()) + if err != nil { + log.F("[vless] error in ResolveUDPAddr: %v", err) + return + } + + pc := NewPktConn(c, tgt) + + go func() { + buf := pool.GetBuffer(proxy.UDPBufSize) + defer pool.PutBuffer(buf) + for { + n, _, err := pc.ReadFrom(buf) + if err != nil { + log.F("[trojan] read error: %s\n", err) + return + } + + _, err = rc.WriteTo(buf[:n], tgtAddr) + if err != nil { + log.F("[trojan] write rc error: %s\n", err) + return + } + } + }() + + log.F("[trojan] %s <-tcp-> %s - %s <-udp-> %s", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) + + buf := pool.GetBuffer(proxy.UDPBufSize) + defer pool.PutBuffer(buf) + + for { + n, _, err := rc.ReadFrom(buf) + if err != nil { + log.F("[trojan] read rc error: %v", err) + break + } + + _, err = pc.WriteTo(buf[:n], nil) + if err != nil { + log.F("[trojan] write pc error: %v", err) + break + } + } +} diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index 876c4f8..43b9336 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -7,14 +7,13 @@ import ( "crypto/sha256" "crypto/tls" "encoding/hex" + "errors" + "fmt" "net" "net/url" "strings" - "github.com/nadoo/glider/log" - "github.com/nadoo/glider/pool" "github.com/nadoo/glider/proxy" - "github.com/nadoo/glider/proxy/socks" ) // Trojan is a base trojan struct. @@ -26,19 +25,20 @@ type Trojan struct { serverName string skipVerify bool tlsConfig *tls.Config + certFile string + keyFile string } func init() { proxy.RegisterDialer("trojan", NewTrojanDialer) - // proxy.RegisterServer("trojan", NewTrojanServer) + proxy.RegisterServer("trojan", NewTrojanServer) } // NewTrojan returns a trojan proxy. func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { u, err := url.Parse(s) if err != nil { - log.F("[trojan] parse url err: %s", err) - return nil, err + return nil, fmt.Errorf("parse url err: %s", err) } query := u.Query() @@ -48,6 +48,8 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { addr: u.Host, skipVerify: query.Get("skipVerify") == "true", serverName: query.Get("serverName"), + certFile: query.Get("cert"), + keyFile: query.Get("key"), } if t.serverName == "" { @@ -60,78 +62,14 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { } // pass - hash := sha256.New224() - hash.Write([]byte(u.User.Username())) - hex.Encode(t.pass[:], hash.Sum(nil)) - - t.tlsConfig = &tls.Config{ - ServerName: t.serverName, - InsecureSkipVerify: t.skipVerify, - NextProtos: []string{"http/1.1"}, - ClientSessionCache: tls.NewLRUClientSessionCache(64), - MinVersion: tls.VersionTLS12, + pass := u.User.Username() + if pass == "" { + return nil, errors.New("[trojan] password must be specified") } + hash := sha256.New224() + hash.Write([]byte(pass)) + hex.Encode(t.pass[:], hash.Sum(nil)) + return t, nil } - -// NewTrojanDialer returns a trojan proxy dialer. -func NewTrojanDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { - return NewTrojan(s, d, nil) -} - -// Addr returns forwarder's address. -func (s *Trojan) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// Dial connects to the address addr on the network net via the proxy. -func (s *Trojan) Dial(network, addr string) (net.Conn, error) { - return s.dial(network, addr) -} - -func (s *Trojan) dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - log.F("[trojan]: dial to %s error: %s", s.addr, err) - return nil, err - } - - tlsConn := tls.Client(rc, s.tlsConfig) - if err := tlsConn.Handshake(); err != nil { - return nil, err - } - - buf := pool.GetWriteBuffer() - defer pool.PutWriteBuffer(buf) - - buf.Write(s.pass[:]) - buf.WriteString("\r\n") - - cmd := socks.CmdConnect - if network == "udp" { - cmd = socks.CmdUDPAssociate - } - buf.WriteByte(cmd) - - buf.Write(socks.ParseAddr(addr)) - buf.WriteString("\r\n") - _, err = tlsConn.Write(buf.Bytes()) - - return tlsConn, err -} - -// DialUDP connects to the given address via the proxy. -func (s *Trojan) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { - c, err := s.dial("udp", addr) - if err != nil { - return nil, nil, err - } - - pkc := NewPktConn(c, socks.ParseAddr(addr)) - // TODO: check the addr in return value - return pkc, nil, nil -} diff --git a/proxy/vless/packet.go b/proxy/vless/packet.go index 8929f3f..628e8e3 100644 --- a/proxy/vless/packet.go +++ b/proxy/vless/packet.go @@ -30,11 +30,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { // Payload n, err := io.ReadFull(pc.Conn, b[:length]) - if err != nil { - return n, nil, err - } - - return n, nil, nil + return n, nil, err } // WriteTo implements the necessary function of net.PacketConn. diff --git a/proxy/vless/server.go b/proxy/vless/server.go index bdc062a..02112b4 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -50,7 +50,6 @@ func (s *VLess) Serve(c net.Conn) { } var fallback bool - var dialer proxy.Dialer target := s.fallback wbuf := pool.GetWriteBuffer() @@ -67,7 +66,7 @@ func (s *VLess) Serve(c net.Conn) { } network := "tcp" - dialer = s.proxy.NextDialer(target) + dialer := s.proxy.NextDialer(target) if !fallback { c = NewServerConn(c) target, err = ReadAddrString(c) @@ -75,6 +74,7 @@ func (s *VLess) Serve(c net.Conn) { log.F("[vless] get target error: %v", err) return } + dialer = s.proxy.NextDialer(target) if cmd == CmdUDP { // there is no upstream proxy, just serve it