mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
trojan: support listen as trojan server
This commit is contained in:
parent
6eda2b79c8
commit
175ef16a5c
13
README.md
13
README.md
@ -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
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
4
go.mod
@ -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
8
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.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=
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
86
proxy/trojan/client.go
Normal 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
197
proxy/trojan/server.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user