mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +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)
|
||||
|
||||
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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user