mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
trojan: support fallback
This commit is contained in:
parent
6fff126e4b
commit
a1ff92201c
@ -177,8 +177,8 @@ Trojan client scheme:
|
|||||||
trojanc://pass@host:port (cleartext, without TLS)
|
trojanc://pass@host:port (cleartext, without TLS)
|
||||||
|
|
||||||
Trojan server scheme:
|
Trojan server scheme:
|
||||||
trojan://pass@host:port?cert=PATH&key=PATH
|
trojan://pass@host:port?cert=PATH&key=PATH[&fallback=127.0.0.1]
|
||||||
trojanc://pass@host:port (cleartext, without TLS)
|
trojanc://pass@host:port[?fallback=127.0.0.1] (cleartext, without TLS)
|
||||||
|
|
||||||
Available securities for vmess:
|
Available securities for vmess:
|
||||||
none, aes-128-gcm, chacha20-poly1305
|
none, aes-128-gcm, chacha20-poly1305
|
||||||
|
@ -174,8 +174,8 @@ func usage() {
|
|||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(w, "Trojan server scheme:\n")
|
fmt.Fprintf(w, "Trojan server scheme:\n")
|
||||||
fmt.Fprintf(w, " trojan://pass@host:port?cert=PATH&key=PATH\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 (cleartext, without TLS)\n")
|
fmt.Fprintf(w, " trojanc://pass@host:port[?fallback=127.0.0.1] (cleartext, without TLS)\n")
|
||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(w, "Available securities for vmess:\n")
|
fmt.Fprintf(w, "Available securities for vmess:\n")
|
||||||
|
@ -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
|
# listen=tls://:1234?cert=/path/to/cert&key=/path/to/key,vless://UUID@?fallback=127.0.0.1:80
|
||||||
|
|
||||||
# trojan server
|
# 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
|
# FORWARDERS
|
||||||
# ----------
|
# ----------
|
||||||
@ -119,6 +122,9 @@ listen=socks5://:1080
|
|||||||
# trojan as forwarder
|
# trojan as forwarder
|
||||||
# forward=trojan://PASSWORD@1.1.1.1:8080[?serverName=SERVERNAME][&skipVerify=true]
|
# 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
|
# vless forwarder
|
||||||
# forward=vless://5a146038-0b56-4e95-b1dc-5c6f5a32cd98@1.1.1.1:443
|
# forward=vless://5a146038-0b56-4e95-b1dc-5c6f5a32cd98@1.1.1.1:443
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ func NewClearTextDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.clearText = true
|
t.withTLS = false
|
||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ func (s *Trojan) dial(network, addr string) (net.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.clearText {
|
if s.withTLS {
|
||||||
tlsConn := tls.Client(rc, s.tlsConfig)
|
tlsConn := tls.Client(rc, s.tlsConfig)
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -23,7 +23,7 @@ func NewClearTextServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.clearText = true
|
t.withTLS = false
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +35,10 @@ func NewTrojanServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
|||||||
return nil, err
|
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)
|
cert, err := tls.LoadX509KeyPair(t.certFile, t.keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[trojan] unable to load cert: %s, key %s", t.certFile, t.keyFile)
|
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()
|
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 {
|
for {
|
||||||
c, err := l.Accept()
|
c, err := l.Accept()
|
||||||
@ -79,7 +83,7 @@ func (s *Trojan) Serve(c net.Conn) {
|
|||||||
c.SetKeepAlive(true)
|
c.SetKeepAlive(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.clearText {
|
if s.withTLS {
|
||||||
tlsConn := tls.Server(c, s.tlsConfig)
|
tlsConn := tls.Server(c, s.tlsConfig)
|
||||||
err := tlsConn.Handshake()
|
err := tlsConn.Handshake()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,9 +93,15 @@ func (s *Trojan) Serve(c net.Conn) {
|
|||||||
c = tlsConn
|
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 {
|
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
|
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
|
// pass: 56, "\r\n": 2, cmd: 1
|
||||||
buf := pool.GetBuffer(59)
|
buf := pool.GetBuffer(59)
|
||||||
defer pool.PutBuffer(buf)
|
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
|
return socks.CmdError, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,13 +175,13 @@ func (s *Trojan) readHeader(c net.Conn) (byte, socks.Addr, error) {
|
|||||||
cmd := byte(buf[58])
|
cmd := byte(buf[58])
|
||||||
|
|
||||||
// target
|
// target
|
||||||
tgt, err := socks.ReadAddr(c)
|
tgt, err := socks.ReadAddr(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmd, nil, fmt.Errorf("read target address error: %v", err)
|
return cmd, nil, fmt.Errorf("read target address error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// "\r\n", 2bytes
|
// "\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
|
return socks.CmdError, tgt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +235,7 @@ func (s *Trojan) ServeUoT(c net.Conn, tgt socks.Addr) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteTo addr can be nil because the PktConn has it's own target, see packet.go
|
||||||
_, err = pc.WriteTo(buf[:n], nil)
|
_, err = pc.WriteTo(buf[:n], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[trojan] write pc error: %v", err)
|
log.F("[trojan] write pc error: %v", err)
|
||||||
|
@ -22,16 +22,13 @@ type Trojan struct {
|
|||||||
proxy proxy.Proxy
|
proxy proxy.Proxy
|
||||||
addr string
|
addr string
|
||||||
pass [56]byte
|
pass [56]byte
|
||||||
|
withTLS bool
|
||||||
clearText bool
|
|
||||||
|
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
|
|
||||||
serverName string
|
serverName string
|
||||||
skipVerify bool
|
skipVerify bool
|
||||||
|
|
||||||
certFile string
|
certFile string
|
||||||
keyFile string
|
keyFile string
|
||||||
|
fallback string
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -53,10 +50,12 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
|||||||
dialer: d,
|
dialer: d,
|
||||||
proxy: p,
|
proxy: p,
|
||||||
addr: u.Host,
|
addr: u.Host,
|
||||||
|
withTLS: true,
|
||||||
skipVerify: query.Get("skipVerify") == "true",
|
skipVerify: query.Get("skipVerify") == "true",
|
||||||
serverName: query.Get("serverName"),
|
serverName: query.Get("serverName"),
|
||||||
certFile: query.Get("cert"),
|
certFile: query.Get("cert"),
|
||||||
keyFile: query.Get("key"),
|
keyFile: query.Get("key"),
|
||||||
|
// fallback: "127.0.0.1:80",
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.serverName == "" {
|
if t.serverName == "" {
|
||||||
@ -78,5 +77,9 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
|||||||
hash.Write([]byte(pass))
|
hash.Write([]byte(pass))
|
||||||
hex.Encode(t.pass[:], hash.Sum(nil))
|
hex.Encode(t.pass[:], hash.Sum(nil))
|
||||||
|
|
||||||
|
if fb := u.Query().Get("fallback"); fb != "" {
|
||||||
|
t.fallback = fb
|
||||||
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
@ -49,32 +49,20 @@ func (s *VLess) Serve(c net.Conn) {
|
|||||||
c.SetKeepAlive(true)
|
c.SetKeepAlive(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fallback bool
|
headBuf := pool.GetWriteBuffer()
|
||||||
target := s.fallback
|
defer pool.PutWriteBuffer(headBuf)
|
||||||
|
|
||||||
wbuf := pool.GetWriteBuffer()
|
cmd, target, err := s.readHeader(io.TeeReader(c, headBuf))
|
||||||
defer pool.PutWriteBuffer(wbuf)
|
|
||||||
|
|
||||||
cmd, err := s.readHeader(io.TeeReader(c, wbuf))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s.fallback == "" {
|
|
||||||
log.F("[vless] verify header from %s error: %v", c.RemoteAddr(), err)
|
log.F("[vless] verify header from %s error: %v", c.RemoteAddr(), err)
|
||||||
return
|
if s.fallback != "" {
|
||||||
|
s.serveFallback(c, s.fallback, headBuf)
|
||||||
}
|
}
|
||||||
fallback = true
|
return
|
||||||
log.F("[vless] verify header from %s error: %v, fallback to %s", c.RemoteAddr(), err, s.fallback)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
network := "tcp"
|
network := "tcp"
|
||||||
dialer := s.proxy.NextDialer(target)
|
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
|
|
||||||
}
|
|
||||||
dialer = s.proxy.NextDialer(target)
|
|
||||||
|
|
||||||
if cmd == CmdUDP {
|
if cmd == CmdUDP {
|
||||||
// there is no upstream proxy, just serve it
|
// there is no upstream proxy, just serve it
|
||||||
@ -84,7 +72,6 @@ func (s *VLess) Serve(c net.Conn) {
|
|||||||
}
|
}
|
||||||
network = "udp"
|
network = "udp"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
rc, err := dialer.Dial(network, target)
|
rc, err := dialer.Dial(network, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -93,17 +80,9 @@ func (s *VLess) Serve(c net.Conn) {
|
|||||||
}
|
}
|
||||||
defer rc.Close()
|
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())
|
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)
|
log.F("[vless] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), target, dialer.Addr(), err)
|
||||||
// record remote conn failure only
|
// record remote conn failure only
|
||||||
if !strings.Contains(err.Error(), s.addr) {
|
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.
|
// ServeUoT serves udp over tcp requests.
|
||||||
func (s *VLess) ServeUoT(c net.Conn, tgt string) {
|
func (s *VLess) ServeUoT(c net.Conn, tgt string) {
|
||||||
rc, err := net.ListenPacket("udp", "")
|
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.
|
// ServerConn is a vless client connection.
|
||||||
type ServerConn struct {
|
type ServerConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
Loading…
Reference in New Issue
Block a user