mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +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
|
||||
|ssr | | |√| |client only
|
||||
|ssh | | |√| |client only
|
||||
|trojan | | |√|√|client only
|
||||
|trojan |√|√|√|√|client & server
|
||||
|vless |√|√|√|√|client & server
|
||||
|vmess | | |√| |client only
|
||||
|redir |√| | | |linux only
|
||||
@ -88,7 +88,7 @@ glider -h
|
||||
<summary>click to see details</summary>
|
||||
|
||||
```bash
|
||||
./glider 0.11.2 usage:
|
||||
./glider 0.12.0 usage:
|
||||
-checkdisabledonly
|
||||
check disabled fowarders only
|
||||
-checkinterval int
|
||||
@ -106,7 +106,7 @@ glider -h
|
||||
-dnsalwaystcp
|
||||
always use tcp to query upstream dns servers no matter there is a forwarder or not
|
||||
-dnscachesize int
|
||||
size of CACHE (default 1024)
|
||||
size of CACHE (default 4096)
|
||||
-dnsmaxttl int
|
||||
maximum TTL value for entries in the CACHE(seconds) (default 1800)
|
||||
-dnsminttl int
|
||||
@ -141,7 +141,7 @@ glider -h
|
||||
verbose mode
|
||||
|
||||
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
|
||||
|
||||
Socks5 scheme:
|
||||
@ -171,9 +171,12 @@ VMess scheme:
|
||||
VLESS scheme:
|
||||
vless://uuid@host:port[?fallback=127.0.0.1:80]
|
||||
|
||||
Trojan scheme:
|
||||
Trojan client scheme:
|
||||
trojan://pass@host:port[?serverName=SERVERNAME][&skipVerify=true]
|
||||
|
||||
Trojan server scheme:
|
||||
trojan://pass@host:port?cert=PATH&key=PATH
|
||||
|
||||
Available securities for vmess:
|
||||
none, aes-128-gcm, chacha20-poly1305
|
||||
|
||||
|
@ -130,7 +130,7 @@ func usage() {
|
||||
fmt.Fprintf(w, "\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, "\n")
|
||||
|
||||
@ -168,10 +168,14 @@ func usage() {
|
||||
fmt.Fprintf(w, " vless://uuid@host:port[?fallback=127.0.0.1:80]\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, "\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, " none, aes-128-gcm, chacha20-poly1305\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
@ -72,6 +72,15 @@ listen=socks5://:1080
|
||||
# socks5 over kcp
|
||||
# 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, we can setup multiple forwarders.
|
||||
@ -216,6 +225,9 @@ dnsmaxttl=1800
|
||||
# minimum TTL value for entries in the CACHE(seconds)
|
||||
dnsminttl=0
|
||||
|
||||
# size of CACHE
|
||||
dnscachesize=4096
|
||||
|
||||
# custom records
|
||||
dnsrecord=www.example.com/1.2.3.4
|
||||
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)
|
||||
|
||||
// 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.
|
||||
if len(c.cache) > c.size {
|
||||
c.removeTail()
|
||||
|
@ -76,7 +76,7 @@ func (c *Client) Exchange(reqBytes []byte, clientAddr string, preferTCP bool) ([
|
||||
go func(qname string, reqBytes []byte, preferTCP bool) {
|
||||
defer pool.PutBuffer(reqBytes)
|
||||
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)
|
||||
}
|
||||
@ -209,12 +209,13 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) (
|
||||
|
||||
// exchangeTCP exchange with server over tcp.
|
||||
func (c *Client) exchangeTCP(rc net.Conn, reqBytes []byte) ([]byte, error) {
|
||||
reqLen := pool.GetBuffer(2)
|
||||
defer pool.PutBuffer(reqLen)
|
||||
buf := pool.GetBuffer(2 + len(reqBytes))
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -143,12 +143,15 @@ func (s *Server) ServeTCP(c net.Conn) {
|
||||
return
|
||||
}
|
||||
|
||||
respLen := pool.GetBuffer(2)
|
||||
defer pool.PutBuffer(respLen)
|
||||
binary.BigEndian.PutUint16(respLen, uint16(len(respBytes)))
|
||||
buf := pool.GetBuffer(2 + len(respBytes))
|
||||
defer pool.PutBuffer(buf)
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -9,11 +9,11 @@ require (
|
||||
github.com/nadoo/go-shadowsocks2 v0.1.2
|
||||
github.com/nadoo/ipset v0.3.0
|
||||
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/net v0.0.0-20201009032441-dbdefad45b89 // 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
|
||||
)
|
||||
|
||||
|
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 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.17 h1:bkdaqtER0PMlP05BBHfu6W+71kt/NwbAk93KH7F78Ck=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||
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/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
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/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-20201009032223-96877f285f7e h1:G1acLyqfyttmexrW7XPhzsaS8m6s+P9XsW9djwh10s4=
|
||||
golang.org/x/tools v0.0.0-20201009032223-96877f285f7e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91 h1:UNUk0ao5UA0V4v2wikQWc4U+yG5UGoWku8MHs27mMqs=
|
||||
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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
|
||||
const (
|
||||
CmdError byte = 0
|
||||
CmdConnect byte = 1
|
||||
CmdBind byte = 2
|
||||
CmdUDPAssociate byte = 3
|
||||
|
@ -73,11 +73,12 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
// +----+------+------+----------+----------+----------+
|
||||
// | 2 | 1 | 1 | Variable | 2 | Variable |
|
||||
// +----+------+------+----------+----------+----------+
|
||||
tgtAddr := socks.SplitAddr(buf[3:])
|
||||
tgtAddr := socks.SplitAddr(buf[3:n])
|
||||
if tgtAddr == nil {
|
||||
return n, raddr, errors.New("can not get addr")
|
||||
}
|
||||
copy(b, buf[3+len(tgtAddr):])
|
||||
|
||||
n = copy(b, buf[3+len(tgtAddr):n])
|
||||
|
||||
//test
|
||||
if pc.writeAddr == nil {
|
||||
@ -88,7 +89,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
pc.tgtAddr = tgtAddr
|
||||
}
|
||||
|
||||
return n - len(tgtAddr) - 3, raddr, err
|
||||
return n, raddr, err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
tgtAddr := socks.SplitAddr(buf)
|
||||
tgtAddr := socks.SplitAddr(buf[:n])
|
||||
if tgtAddr == nil {
|
||||
return n, raddr, errors.New("can not get addr")
|
||||
}
|
||||
copy(b, buf[len(tgtAddr):])
|
||||
|
||||
n = copy(b, buf[len(tgtAddr):n])
|
||||
|
||||
//test
|
||||
if pc.writeAddr == nil {
|
||||
@ -57,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
pc.tgtAddr = tgtAddr
|
||||
}
|
||||
|
||||
return n - len(tgtAddr), raddr, err
|
||||
return n, raddr, err
|
||||
}
|
||||
|
||||
// WriteTo overrides the original function from net.PacketConn
|
||||
|
@ -24,7 +24,6 @@ type TLS struct {
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
cert stdtls.Certificate
|
||||
|
||||
server proxy.Server
|
||||
}
|
||||
@ -76,7 +75,7 @@ func NewTLSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
||||
ServerName: p.serverName,
|
||||
InsecureSkipVerify: p.skipVerify,
|
||||
ClientSessionCache: stdtls.NewLRUClientSessionCache(64),
|
||||
MinVersion: stdtls.VersionTLS10,
|
||||
MinVersion: stdtls.VersionTLS12,
|
||||
}
|
||||
|
||||
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/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/log"
|
||||
"github.com/nadoo/glider/pool"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
"github.com/nadoo/glider/proxy/socks"
|
||||
)
|
||||
|
||||
// Trojan is a base trojan struct.
|
||||
@ -26,19 +25,20 @@ type Trojan struct {
|
||||
serverName string
|
||||
skipVerify bool
|
||||
tlsConfig *tls.Config
|
||||
certFile string
|
||||
keyFile string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("trojan", NewTrojanDialer)
|
||||
// proxy.RegisterServer("trojan", NewTrojanServer)
|
||||
proxy.RegisterServer("trojan", NewTrojanServer)
|
||||
}
|
||||
|
||||
// NewTrojan returns a trojan proxy.
|
||||
func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("[trojan] parse url err: %s", err)
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("parse url err: %s", err)
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
@ -48,6 +48,8 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
||||
addr: u.Host,
|
||||
skipVerify: query.Get("skipVerify") == "true",
|
||||
serverName: query.Get("serverName"),
|
||||
certFile: query.Get("cert"),
|
||||
keyFile: query.Get("key"),
|
||||
}
|
||||
|
||||
if t.serverName == "" {
|
||||
@ -60,78 +62,14 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
|
||||
}
|
||||
|
||||
// pass
|
||||
hash := sha256.New224()
|
||||
hash.Write([]byte(u.User.Username()))
|
||||
hex.Encode(t.pass[:], hash.Sum(nil))
|
||||
|
||||
t.tlsConfig = &tls.Config{
|
||||
ServerName: t.serverName,
|
||||
InsecureSkipVerify: t.skipVerify,
|
||||
NextProtos: []string{"http/1.1"},
|
||||
ClientSessionCache: tls.NewLRUClientSessionCache(64),
|
||||
MinVersion: tls.VersionTLS12,
|
||||
pass := u.User.Username()
|
||||
if pass == "" {
|
||||
return nil, errors.New("[trojan] password must be specified")
|
||||
}
|
||||
|
||||
hash := sha256.New224()
|
||||
hash.Write([]byte(pass))
|
||||
hex.Encode(t.pass[:], hash.Sum(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,11 +30,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
|
||||
// Payload
|
||||
n, err := io.ReadFull(pc.Conn, b[:length])
|
||||
if err != nil {
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
return n, nil, nil
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// WriteTo implements the necessary function of net.PacketConn.
|
||||
|
@ -50,7 +50,6 @@ func (s *VLess) Serve(c net.Conn) {
|
||||
}
|
||||
|
||||
var fallback bool
|
||||
var dialer proxy.Dialer
|
||||
target := s.fallback
|
||||
|
||||
wbuf := pool.GetWriteBuffer()
|
||||
@ -67,7 +66,7 @@ func (s *VLess) Serve(c net.Conn) {
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
dialer = s.proxy.NextDialer(target)
|
||||
dialer := s.proxy.NextDialer(target)
|
||||
if !fallback {
|
||||
c = NewServerConn(c)
|
||||
target, err = ReadAddrString(c)
|
||||
@ -75,6 +74,7 @@ func (s *VLess) Serve(c net.Conn) {
|
||||
log.F("[vless] get target error: %v", err)
|
||||
return
|
||||
}
|
||||
dialer = s.proxy.NextDialer(target)
|
||||
|
||||
if cmd == CmdUDP {
|
||||
// there is no upstream proxy, just serve it
|
||||
|
Loading…
Reference in New Issue
Block a user