trojan: support listen as trojan server

This commit is contained in:
nadoo 2020-10-10 19:04:33 +08:00
parent 6eda2b79c8
commit 175ef16a5c
17 changed files with 357 additions and 115 deletions

View File

@ -52,7 +52,7 @@ we can set up local listeners as proxy servers, and forward requests to internet
|ss |√|√|√|√|client & server |ss |√|√|√|√|client & server
|ssr | | |√| |client only |ssr | | |√| |client only
|ssh | | |√| |client only |ssh | | |√| |client only
|trojan | | |√|√|client only |trojan |√|√|√|√|client & server
|vless |√|√|√|√|client & server |vless |√|√|√|√|client & server
|vmess | | |√| |client only |vmess | | |√| |client only
|redir |√| | | |linux only |redir |√| | | |linux only
@ -88,7 +88,7 @@ glider -h
<summary>click to see details</summary> <summary>click to see details</summary>
```bash ```bash
./glider 0.11.2 usage: ./glider 0.12.0 usage:
-checkdisabledonly -checkdisabledonly
check disabled fowarders only check disabled fowarders only
-checkinterval int -checkinterval int
@ -106,7 +106,7 @@ glider -h
-dnsalwaystcp -dnsalwaystcp
always use tcp to query upstream dns servers no matter there is a forwarder or not always use tcp to query upstream dns servers no matter there is a forwarder or not
-dnscachesize int -dnscachesize int
size of CACHE (default 1024) size of CACHE (default 4096)
-dnsmaxttl int -dnsmaxttl int
maximum TTL value for entries in the CACHE(seconds) (default 1800) maximum TTL value for entries in the CACHE(seconds) (default 1800)
-dnsminttl int -dnsminttl int
@ -141,7 +141,7 @@ glider -h
verbose mode verbose mode
Available schemes: 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 forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs
Socks5 scheme: Socks5 scheme:
@ -171,9 +171,12 @@ VMess scheme:
VLESS scheme: VLESS scheme:
vless://uuid@host:port[?fallback=127.0.0.1:80] vless://uuid@host:port[?fallback=127.0.0.1:80]
Trojan scheme: Trojan client scheme:
trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true] trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true]
Trojan server scheme:
trojan://pass@host:port?cert=PATH&key=PATH
Available securities for vmess: Available securities for vmess:
none, aes-128-gcm, chacha20-poly1305 none, aes-128-gcm, chacha20-poly1305

View File

@ -130,7 +130,7 @@ func usage() {
fmt.Fprintf(w, "\n") fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Available schemes:\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, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan tls ws unix kcp simple-obfs\n")
fmt.Fprintf(w, "\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, " vless://uuid@host:port[?fallback=127.0.0.1:80]\n")
fmt.Fprintf(w, "\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, " trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true]\n")
fmt.Fprintf(w, "\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, "Available securities for vmess:\n")
fmt.Fprintf(w, " none, aes-128-gcm, chacha20-poly1305\n") fmt.Fprintf(w, " none, aes-128-gcm, chacha20-poly1305\n")
fmt.Fprintf(w, "\n") fmt.Fprintf(w, "\n")

View File

@ -72,6 +72,15 @@ listen=socks5://:1080
# socks5 over kcp # socks5 over kcp
# listen=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,socks5:// # 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
# ---------- # ----------
# Forwarders, we can setup multiple forwarders. # Forwarders, we can setup multiple forwarders.
@ -216,6 +225,9 @@ dnsmaxttl=1800
# minimum TTL value for entries in the CACHE(seconds) # minimum TTL value for entries in the CACHE(seconds)
dnsminttl=0 dnsminttl=0
# size of CACHE
dnscachesize=4096
# custom records # custom records
dnsrecord=www.example.com/1.2.3.4 dnsrecord=www.example.com/1.2.3.4
dnsrecord=www.example.com/2606:2800:220:1:248:1893:25c8:1946 dnsrecord=www.example.com/2606:2800:220:1:248:1893:25c8:1946

View File

@ -83,7 +83,7 @@ func (c *LruCache) Set(k string, v []byte, ttl int) {
c.putToHead(k, v, exp) 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. // but it doesn't matter in our environment.
if len(c.cache) > c.size { if len(c.cache) > c.size {
c.removeTail() c.removeTail()

View File

@ -76,7 +76,7 @@ func (c *Client) Exchange(reqBytes []byte, clientAddr string, preferTCP bool) ([
go func(qname string, reqBytes []byte, preferTCP bool) { go func(qname string, reqBytes []byte, preferTCP bool) {
defer pool.PutBuffer(reqBytes) defer pool.PutBuffer(reqBytes)
if dnsServer, network, dialerAddr, respBytes, err := c.exchange(qname, reqBytes, preferTCP); err == nil { 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) }(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. // exchangeTCP exchange with server over tcp.
func (c *Client) exchangeTCP(rc net.Conn, reqBytes []byte) ([]byte, error) { func (c *Client) exchangeTCP(rc net.Conn, reqBytes []byte) ([]byte, error) {
reqLen := pool.GetBuffer(2) buf := pool.GetBuffer(2 + len(reqBytes))
defer pool.PutBuffer(reqLen) 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 return nil, err
} }

View File

@ -143,12 +143,15 @@ func (s *Server) ServeTCP(c net.Conn) {
return return
} }
respLen := pool.GetBuffer(2) buf := pool.GetBuffer(2 + len(respBytes))
defer pool.PutBuffer(respLen) defer pool.PutBuffer(buf)
binary.BigEndian.PutUint16(respLen, uint16(len(respBytes)))
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) log.F("[dns-tcp] error in write respBytes: %s", err)
return return
} }
} }

4
go.mod
View File

@ -9,11 +9,11 @@ require (
github.com/nadoo/go-shadowsocks2 v0.1.2 github.com/nadoo/go-shadowsocks2 v0.1.2
github.com/nadoo/ipset v0.3.0 github.com/nadoo/ipset v0.3.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 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/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/net v0.0.0-20201009032441-dbdefad45b89 // indirect golang.org/x/net v0.0.0-20201009032441-dbdefad45b89 // indirect
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // 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 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
) )

8
go.sum
View File

@ -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.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 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.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.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0= 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 h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= 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= 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 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-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-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-20201009162240-fcf82128ed91 h1:UNUk0ao5UA0V4v2wikQWc4U+yG5UGoWku8MHs27mMqs=
golang.org/x/tools v0.0.0-20201009032223-96877f285f7e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

View File

@ -15,6 +15,7 @@ const (
// SOCKS request commands as defined in RFC 1928 section 4 // SOCKS request commands as defined in RFC 1928 section 4
const ( const (
CmdError byte = 0
CmdConnect byte = 1 CmdConnect byte = 1
CmdBind byte = 2 CmdBind byte = 2
CmdUDPAssociate byte = 3 CmdUDPAssociate byte = 3

View File

@ -73,11 +73,12 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
// +----+------+------+----------+----------+----------+ // +----+------+------+----------+----------+----------+
// | 2 | 1 | 1 | Variable | 2 | Variable | // | 2 | 1 | 1 | Variable | 2 | Variable |
// +----+------+------+----------+----------+----------+ // +----+------+------+----------+----------+----------+
tgtAddr := socks.SplitAddr(buf[3:]) tgtAddr := socks.SplitAddr(buf[3:n])
if tgtAddr == nil { if tgtAddr == nil {
return n, raddr, errors.New("can not get addr") return n, raddr, errors.New("can not get addr")
} }
copy(b, buf[3+len(tgtAddr):])
n = copy(b, buf[3+len(tgtAddr):n])
//test //test
if pc.writeAddr == nil { if pc.writeAddr == nil {
@ -88,7 +89,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
pc.tgtAddr = tgtAddr pc.tgtAddr = tgtAddr
} }
return n - len(tgtAddr) - 3, raddr, err return n, raddr, err
} }
// WriteTo overrides the original function from net.PacketConn. // WriteTo overrides the original function from net.PacketConn.

View File

@ -42,11 +42,12 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
return n, raddr, err return n, raddr, err
} }
tgtAddr := socks.SplitAddr(buf) tgtAddr := socks.SplitAddr(buf[:n])
if tgtAddr == nil { if tgtAddr == nil {
return n, raddr, errors.New("can not get addr") return n, raddr, errors.New("can not get addr")
} }
copy(b, buf[len(tgtAddr):])
n = copy(b, buf[len(tgtAddr):n])
//test //test
if pc.writeAddr == nil { if pc.writeAddr == nil {
@ -57,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
pc.tgtAddr = tgtAddr pc.tgtAddr = tgtAddr
} }
return n - len(tgtAddr), raddr, err return n, raddr, err
} }
// WriteTo overrides the original function from net.PacketConn // WriteTo overrides the original function from net.PacketConn

View File

@ -24,7 +24,6 @@ type TLS struct {
certFile string certFile string
keyFile string keyFile string
cert stdtls.Certificate
server proxy.Server server proxy.Server
} }
@ -76,7 +75,7 @@ func NewTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
ServerName: p.serverName, ServerName: p.serverName,
InsecureSkipVerify: p.skipVerify, InsecureSkipVerify: p.skipVerify,
ClientSessionCache: stdtls.NewLRUClientSessionCache(64), ClientSessionCache: stdtls.NewLRUClientSessionCache(64),
MinVersion: stdtls.VersionTLS10, MinVersion: stdtls.VersionTLS12,
} }
return p, err return p, err

86
proxy/trojan/client.go Normal file
View File

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

197
proxy/trojan/server.go Normal file
View File

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

View File

@ -7,14 +7,13 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"errors"
"fmt"
"net" "net"
"net/url" "net/url"
"strings" "strings"
"github.com/nadoo/glider/log"
"github.com/nadoo/glider/pool"
"github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy"
"github.com/nadoo/glider/proxy/socks"
) )
// Trojan is a base trojan struct. // Trojan is a base trojan struct.
@ -26,19 +25,20 @@ type Trojan struct {
serverName string serverName string
skipVerify bool skipVerify bool
tlsConfig *tls.Config tlsConfig *tls.Config
certFile string
keyFile string
} }
func init() { func init() {
proxy.RegisterDialer("trojan", NewTrojanDialer) proxy.RegisterDialer("trojan", NewTrojanDialer)
// proxy.RegisterServer("trojan", NewTrojanServer) proxy.RegisterServer("trojan", NewTrojanServer)
} }
// NewTrojan returns a trojan proxy. // NewTrojan returns a trojan proxy.
func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
u, err := url.Parse(s) u, err := url.Parse(s)
if err != nil { if err != nil {
log.F("[trojan] parse url err: %s", err) return nil, fmt.Errorf("parse url err: %s", err)
return nil, err
} }
query := u.Query() query := u.Query()
@ -48,6 +48,8 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
addr: u.Host, addr: u.Host,
skipVerify: query.Get("skipVerify") == "true", skipVerify: query.Get("skipVerify") == "true",
serverName: query.Get("serverName"), serverName: query.Get("serverName"),
certFile: query.Get("cert"),
keyFile: query.Get("key"),
} }
if t.serverName == "" { if t.serverName == "" {
@ -60,78 +62,14 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
} }
// pass // pass
hash := sha256.New224() pass := u.User.Username()
hash.Write([]byte(u.User.Username())) if pass == "" {
hex.Encode(t.pass[:], hash.Sum(nil)) return nil, errors.New("[trojan] password must be specified")
t.tlsConfig = &tls.Config{
ServerName: t.serverName,
InsecureSkipVerify: t.skipVerify,
NextProtos: []string{"http/1.1"},
ClientSessionCache: tls.NewLRUClientSessionCache(64),
MinVersion: tls.VersionTLS12,
} }
hash := sha256.New224()
hash.Write([]byte(pass))
hex.Encode(t.pass[:], hash.Sum(nil))
return t, 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
}

View File

@ -30,13 +30,9 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
// Payload // Payload
n, err := io.ReadFull(pc.Conn, b[:length]) n, err := io.ReadFull(pc.Conn, b[:length])
if err != nil {
return n, nil, err return n, nil, err
} }
return n, nil, nil
}
// WriteTo implements the necessary function of net.PacketConn. // WriteTo implements the necessary function of net.PacketConn.
func (pc *PktConn) WriteTo(b []byte, addr net.Addr) (int, error) { func (pc *PktConn) WriteTo(b []byte, addr net.Addr) (int, error) {
buf := pool.GetWriteBuffer() buf := pool.GetWriteBuffer()

View File

@ -50,7 +50,6 @@ func (s *VLess) Serve(c net.Conn) {
} }
var fallback bool var fallback bool
var dialer proxy.Dialer
target := s.fallback target := s.fallback
wbuf := pool.GetWriteBuffer() wbuf := pool.GetWriteBuffer()
@ -67,7 +66,7 @@ func (s *VLess) Serve(c net.Conn) {
} }
network := "tcp" network := "tcp"
dialer = s.proxy.NextDialer(target) dialer := s.proxy.NextDialer(target)
if !fallback { if !fallback {
c = NewServerConn(c) c = NewServerConn(c)
target, err = ReadAddrString(c) target, err = ReadAddrString(c)
@ -75,6 +74,7 @@ func (s *VLess) Serve(c net.Conn) {
log.F("[vless] get target error: %v", err) log.F("[vless] get target error: %v", err)
return 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