diff --git a/README.md b/README.md index 7c54601..616960b 100644 --- a/README.md +++ b/README.md @@ -15,58 +15,26 @@ we can set up local listeners as proxy servers, and forward requests to internet ``` ## Features - -Listen (local proxy server): - -- Socks5 proxy(tcp&udp) -- Http proxy(tcp) -- SS proxy(tcp&udp) -- Linux transparent proxy(iptables redirect) -- TCP tunnel -- UDP tunnel -- UDP over TCP tunnel -- TLS, use it together with above proxy protocols(tcp) -- Unix domain socket, use it together with above proxy protocols(tcp) -- KCP protocol, use it together with above proxy protocols(tcp) - -Forward (local proxy client/upstream proxy server): - -- Socks5 proxy(tcp&udp) -- Http proxy(tcp) -- SS proxy(tcp&udp&uot) -- SSR proxy(tcp) -- VMess proxy(tcp) -- Trojan proxy(tcp) -- TLS, use it together with above proxy protocols(tcp) -- Websocket, use it together with above proxy protocols(tcp) -- Unix domain socket, use it together with above proxy protocols(tcp) -- KCP protocol, use it together with above proxy protocols(tcp) -- Simple-Obfs, use it together with above proxy protocols(tcp) - -DNS Forwarding Server (udp2tcp): - -- DNS Over Proxy -- Listen on UDP and forward dns requests to remote dns server in TCP via forwarders -- Specify different upstream dns server based on destinations(in rule file) -- Tunnel mode: forward to a fixed upstream dns server -- Add resolved IPs to proxy rules -- Add resolved IPs to ipset -- DNS cache -- Custom dns record - -IPSet Management (Linux kernel version >= 2.6.32): - -- Add ip/cidrs from rule files on startup -- Add resolved ips for domains from rule files by dns forwarding server - -General: - +- Proxy Server( -listen ) +- Proxy Client( -forward ) - Http and socks5 on the same port - Forwarder chain - RR/HA/LHA/DH strategy for multiple forwarders - Periodical proxy checking - Rule proxy based on destinations: [Config Examples](config/examples) - Send requests from specific ip/interface +- DNS Forwarding Server: + - DNS Over Proxy + - Listen on UDP and forward dns requests to remote dns server in TCP via forwarders + - Specify different upstream dns server based on destinations(in rule file) + - Tunnel mode: forward to a fixed upstream dns server + - Add resolved IPs to proxy rules + - Add resolved IPs to ipset + - DNS cache + - Custom dns record +- IPSet Management (Linux kernel version >= 2.6.32): + - Add ip/cidrs from rule files on startup + - Add resolved ips for domains from rule files by dns forwarding server TODO: @@ -76,6 +44,25 @@ TODO: - [ ] TUN/TAP device support - [ ] SSH tunnel support (maybe) +### Protocols +Protocol | Listen/TCP | Listen/UDP | Forward/TCP | Forward/UDP +-|-|-|-|- +Socks5 | √ | √ | √ | √ +Http | √ | | √ | +SS | √ | √ | √ | √ +Redir | √ | | | +Tcptun | √ | | | +Udptun | | √ | | +UoTtun | | √ | | +TLS | √ | | √ | +Unix | √ | | √ | +KCP | | √ | √ | +SSR | | | √ | +VMess | | | √ | +Trojan | | | √ | √ +WebSocket | | | √ | +Simple-Obfs | | | √ | + ## Install Binary: diff --git a/common/socks/socks.go b/common/socks/socks.go index 2aa5165..bbab7f1 100644 --- a/common/socks/socks.go +++ b/common/socks/socks.go @@ -13,26 +13,22 @@ const ( AuthPassword = 2 ) -// SOCKS request commands as defined in RFC 1928 section 4. +// SOCKS request commands as defined in RFC 1928 section 4 const ( - CmdConnect = 1 - CmdBind = 2 - CmdUDPAssociate = 3 + CmdConnect byte = 1 + CmdBind byte = 2 + CmdUDPAssociate byte = 3 ) -// SOCKS address types as defined in RFC 1928 section 5. +// SOCKS address types as defined in RFC 1928 section 5 const ( ATypIP4 = 1 ATypDomain = 3 ATypIP6 = 4 ) -const ( - // maximum size of SOCKS address in bytes. - MaxAddrLen = 1 + 1 + 255 + 2 - // minimum size of SOCKS address in bytes. - MinAddrLen = 5 -) +// MaxAddrLen is the maximum size of SOCKS address in bytes +const MaxAddrLen = 1 + 1 + 255 + 2 // Errors are socks5 errors var Errors = []error{ diff --git a/proxy/socks5/packet.go b/proxy/socks5/packet.go index 5e6db45..93e1bb7 100644 --- a/proxy/socks5/packet.go +++ b/proxy/socks5/packet.go @@ -58,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { return n, raddr, err } - if n < socks.MinAddrLen { + if n < 3 { return n, raddr, errors.New("not enough size to get addr") } diff --git a/proxy/ss/packet.go b/proxy/ss/packet.go index dae5a11..a89f61f 100644 --- a/proxy/ss/packet.go +++ b/proxy/ss/packet.go @@ -39,10 +39,6 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { return n, raddr, err } - if n < socks.MinAddrLen { - return n, raddr, errors.New("not enough size to get addr") - } - tgtAddr := socks.SplitAddr(buf) if tgtAddr == nil { return n, raddr, errors.New("can not get addr") diff --git a/proxy/trojan/packet.go b/proxy/trojan/packet.go new file mode 100644 index 0000000..db57018 --- /dev/null +++ b/proxy/trojan/packet.go @@ -0,0 +1,75 @@ +// https://trojan-gfw.github.io/trojan/protocol +// If the connection is a UDP ASSOCIATE, then each UDP packet has the following format: +// +------+----------+----------+--------+---------+----------+ +// | ATYP | DST.ADDR | DST.PORT | Length | CRLF | Payload | +// +------+----------+----------+--------+---------+----------+ +// | 1 | Variable | 2 | 2 | X'0D0A' | Variable | +// +------+----------+----------+--------+---------+----------+ +package trojan + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "net" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/socks" +) + +// PktConn . +type PktConn struct { + net.Conn + + tgtAddr socks.Addr +} + +func NewPktConn(c net.Conn, tgtAddr socks.Addr) *PktConn { + pc := &PktConn{ + Conn: c, + tgtAddr: tgtAddr, + } + return pc +} + +func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { + // ATYP, DST.ADDR, DST.PORT + _, err := socks.ReadAddr(pc.Conn) + if err != nil { + return 0, nil, err + } + + // Length + if _, err = io.ReadFull(pc.Conn, b[:2]); err != nil { + return 0, nil, err + } + + length := int(binary.BigEndian.Uint16(b[:2])) + if length > conn.UDPBufSize { + return 0, nil, errors.New("packet invalid") + } + + // CRLF + if _, err = io.ReadFull(pc.Conn, b[:2]); err != nil { + return 0, nil, err + } + + // Payload + n, err := io.ReadFull(pc.Conn, b[:length]) + if err != nil { + return 0, nil, err + } + + // TODO: check the addr in return value, it's a fake packetConn so the addr is not valid + return n, nil, err +} + +func (pc *PktConn) WriteTo(b []byte, addr net.Addr) (int, error) { + var buf bytes.Buffer + buf.Write(pc.tgtAddr) + binary.Write(&buf, binary.BigEndian, uint16(len(b))) + buf.WriteString("\r\n") + buf.Write(b) + return pc.Write(buf.Bytes()) +} diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index 1f52a41..8c88850 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -32,7 +32,6 @@ import ( "crypto/sha256" "crypto/tls" "encoding/hex" - "errors" "net" "net/url" "strings" @@ -42,7 +41,7 @@ import ( "github.com/nadoo/glider/proxy" ) -// Trojan is a base trojan struct. +// Trojan is a base trojan struct type Trojan struct { dialer proxy.Dialer proxy proxy.Proxy @@ -115,8 +114,13 @@ func (s *Trojan) Addr() string { // 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 } @@ -128,14 +132,28 @@ func (s *Trojan) Dial(network, addr string) (net.Conn, error) { var buf bytes.Buffer buf.Write(s.pass[:]) buf.WriteString("\r\n") - buf.WriteByte(socks.CmdConnect) + + 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) { - return nil, nil, errors.New("trojan client does not support udp now") + c, err := s.dial("udp", addr) + if err != nil { + return nil, nil, err + } + + pkc := NewPktConn(c, socks.ParseAddr(addr)) + + return pkc, nil, nil }