trojan: support fallback

This commit is contained in:
nadoo 2020-10-11 19:53:38 +08:00
parent 6fff126e4b
commit a1ff92201c
7 changed files with 149 additions and 103 deletions

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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