From 8b6739f12c5584fcc1f48e6f1cfc86af4b15f6c4 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Sat, 3 Oct 2020 20:51:27 +0800 Subject: [PATCH] vless: spport server mode --- README.md | 2 +- config.go | 1 + go.mod | 7 +- go.sum | 12 ++-- proxy/socks5/socks5.go | 16 ++--- proxy/ss/ss.go | 10 +-- proxy/trojan/packet.go | 4 +- proxy/trojan/trojan.go | 4 +- proxy/vless/addr.go | 73 +++++++++++++++++++- proxy/vless/client.go | 63 ++++++++++------- proxy/vless/packet.go | 12 ++-- proxy/vless/server.go | 150 +++++++++++++++++++++++++++++++++++++++++ proxy/vless/vless.go | 63 +++++++++-------- service/dhcpd/dhcpd.go | 6 +- service/dhcpd/pool.go | 2 + 15 files changed, 328 insertions(+), 97 deletions(-) create mode 100644 proxy/vless/server.go diff --git a/README.md b/README.md index d442910..9463046 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?style=flat-square&include_prereleases)](https://github.com/nadoo/glider/releases) [![Actions Status](https://img.shields.io/github/workflow/status/nadoo/glider/Build?style=flat-square)](https://github.com/nadoo/glider/actions) -glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset management features(like dnsmasq). +glider is a forward proxy with multiple protocols support, and also a dns/dhcp forwarding server with ipset management features(like dnsmasq). we can set up local listeners as proxy servers, and forward requests to internet via forwarders. diff --git a/config.go b/config.go index a66e10b..5b4233a 100644 --- a/config.go +++ b/config.go @@ -14,6 +14,7 @@ import ( var flag = conflag.New() +// Config is global config struct. type Config struct { Verbose bool diff --git a/go.mod b/go.mod index 0106610..cedbcd0 100644 --- a/go.mod +++ b/go.mod @@ -10,10 +10,9 @@ require ( 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 - golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 - golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect - golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40 // indirect + golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 + golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect + golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 577158a..56c27e9 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977 h1:yH6opeNE+0SY+7pXT4gclZUoKHogXeC2EvOSHGOMGPU= -golang.org/x/crypto v0.0.0-20201002094018-c90954cbb977/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= @@ -141,8 +141,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw= +golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -173,8 +173,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-20201002055958-0d28ed0cbe40 h1:ErPN1Z9An7dXc56pRUCKgWJkjYzc3hE+y15ky9E8qxU= -golang.org/x/tools v0.0.0-20201002055958-0d28ed0cbe40/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d h1:vWQvJ/Z0Lu+9/8oQ/pAYXNzbc7CMnBl+tULGVHOy3oE= +golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/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= diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index de1d532..1072f96 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -154,12 +154,12 @@ func (s *Socks5) Serve(c net.Conn) { func (s *Socks5) ListenAndServeUDP() { lc, err := net.ListenPacket("udp", s.addr) if err != nil { - log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err) + log.F("[socks5] failed to listen on UDP %s: %v", s.addr, err) return } defer lc.Close() - log.F("[socks5-udp] listening UDP on %s", s.addr) + log.F("[socks5] listening UDP on %s", s.addr) var nm sync.Map buf := make([]byte, proxy.UDPBufSize) @@ -169,7 +169,7 @@ func (s *Socks5) ListenAndServeUDP() { n, raddr, err := c.ReadFrom(buf) if err != nil { - log.F("[socks5-udp] remote read error: %v", err) + log.F("[socks5u] remote read error: %v", err) continue } @@ -177,13 +177,13 @@ func (s *Socks5) ListenAndServeUDP() { v, ok := nm.Load(raddr.String()) if !ok && v == nil { if c.tgtAddr == nil { - log.F("[socks5-udp] can not get target address, not a valid request") + log.F("[socks5u] can not get target address, not a valid request") continue } lpc, nextHop, err := s.proxy.DialUDP("udp", c.tgtAddr.String()) if err != nil { - log.F("[socks5-udp] remote dial error: %v", err) + log.F("[socks5u] remote dial error: %v", err) continue } @@ -196,7 +196,7 @@ func (s *Socks5) ListenAndServeUDP() { nm.Delete(raddr.String()) }() - log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) + log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr) } else { pc = v.(*PktConn) @@ -204,11 +204,11 @@ func (s *Socks5) ListenAndServeUDP() { _, err = pc.WriteTo(buf[:n], pc.writeAddr) if err != nil { - log.F("[socks5-udp] remote write error: %v", err) + log.F("[socks5u] remote write error: %v", err) continue } - // log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr) + // log.F("[socks5u] %s <-> %s", raddr, c.tgtAddr) } } diff --git a/proxy/ss/ss.go b/proxy/ss/ss.go index 193e09b..7c836fc 100644 --- a/proxy/ss/ss.go +++ b/proxy/ss/ss.go @@ -117,7 +117,7 @@ func (s *SS) Serve(c net.Conn) { if uot && dialer.Addr() == "DIRECT" { rc, err := net.ListenPacket("udp", "") if err != nil { - log.F("[ss-uottun] UDP remote listen error: %v", err) + log.F("[ssuot] UDP remote listen error: %v", err) } defer rc.Close() @@ -126,7 +126,7 @@ func (s *SS) Serve(c net.Conn) { n, err := c.Read(buf) if err != nil { - log.F("[ss-uottun] error in read: %s\n", err) + log.F("[ssuot] error in read: %s\n", err) return } @@ -135,7 +135,7 @@ func (s *SS) Serve(c net.Conn) { n, _, err = rc.ReadFrom(buf) if err != nil { - log.F("[ss-uottun] read error: %v", err) + log.F("[ssuot] read error: %v", err) } c.Write(buf[:n]) @@ -172,14 +172,14 @@ func (s *SS) Serve(c net.Conn) { func (s *SS) ListenAndServeUDP() { lc, err := net.ListenPacket("udp", s.addr) if err != nil { - log.F("[ssu] failed to listen on %s: %v", s.addr, err) + log.F("[ss] failed to listen on UDP %s: %v", s.addr, err) return } defer lc.Close() lc = s.PacketConn(lc) - log.F("[ssu] listening UDP on %s", s.addr) + log.F("[ss] listening UDP on %s", s.addr) var nm sync.Map buf := make([]byte, proxy.UDPBufSize) diff --git a/proxy/trojan/packet.go b/proxy/trojan/packet.go index 5bf5910..da345ce 100644 --- a/proxy/trojan/packet.go +++ b/proxy/trojan/packet.go @@ -11,7 +11,7 @@ import ( "github.com/nadoo/glider/proxy/socks" ) -// PktConn . +// PktConn is a udp Packet.Conn. type PktConn struct { net.Conn @@ -58,7 +58,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { // Payload n, err := io.ReadFull(pc.Conn, b[:length]) if err != nil { - return 0, nil, err + return n, nil, err } // TODO: check the addr in return value, it's a fake packetConn so the addr is not valid diff --git a/proxy/trojan/trojan.go b/proxy/trojan/trojan.go index b8c8d38..849d907 100644 --- a/proxy/trojan/trojan.go +++ b/proxy/trojan/trojan.go @@ -17,7 +17,7 @@ import ( "github.com/nadoo/glider/proxy/socks" ) -// Trojan is a base trojan struct +// Trojan is a base trojan struct. type Trojan struct { dialer proxy.Dialer proxy proxy.Proxy @@ -67,7 +67,7 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) { t.tlsConfig = &tls.Config{ ServerName: t.serverName, InsecureSkipVerify: t.skipVerify, - NextProtos: []string{"http/1.1", "h2"}, + NextProtos: []string{"http/1.1"}, ClientSessionCache: tls.NewLRUClientSessionCache(64), MinVersion: tls.VersionTLS10, } diff --git a/proxy/vless/addr.go b/proxy/vless/addr.go index 5ecd683..00fa57d 100644 --- a/proxy/vless/addr.go +++ b/proxy/vless/addr.go @@ -1,8 +1,12 @@ package vless import ( + "encoding/binary" + "io" "net" "strconv" + + "github.com/nadoo/glider/pool" ) // Atyp is vless addr type. @@ -19,6 +23,9 @@ const ( // Addr is vless addr. type Addr []byte +// MaxHostLen is the maximum size of host in bytes. +const MaxHostLen = 255 + // Port is vless addr port. type Port uint16 @@ -43,7 +50,7 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) { copy(addr[:], ip) } } else { - if len(host) > 255 { + if len(host) > MaxHostLen { return 0, nil, 0, err } addr = make([]byte, 1+len(host)) @@ -59,3 +66,67 @@ func ParseAddr(s string) (Atyp, Addr, Port, error) { return atyp, addr, Port(portnum), err } + +// ReadAddr reads just enough bytes from r to get addr. +func ReadAddr(r io.Reader) (atyp Atyp, host Addr, port Port, err error) { + buf := pool.GetBuffer(2) + defer pool.PutBuffer(buf) + + // port + _, err = io.ReadFull(r, buf[:2]) + if err != nil { + return + } + port = Port(binary.BigEndian.Uint16(buf[:2])) + + // atyp + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + atyp = Atyp(buf[0]) + + switch atyp { + case AtypIP4: + host = make([]byte, net.IPv4len) + _, err = io.ReadFull(r, host) + return + case AtypIP6: + host = make([]byte, net.IPv6len) + _, err = io.ReadFull(r, host) + return + case AtypDomain: + _, err = io.ReadFull(r, buf[:1]) + if err != nil { + return + } + host = make([]byte, int(buf[0])) + _, err = io.ReadFull(r, host) + return + } + + return +} + +// ReadAddrString reads just enough bytes from r to get addr string. +func ReadAddrString(r io.Reader) (string, error) { + atyp, host, port, err := ReadAddr(r) + if err != nil { + return "", err + } + return AddrString(atyp, host, port), nil +} + +// AddrString returns a addr string inf format "host:port". +func AddrString(atyp Atyp, addr Addr, port Port) string { + var host string + + switch atyp { + case AtypIP4, AtypIP6: + host = net.IP(addr).String() + case AtypDomain: + host = string(addr) + } + + return net.JoinHostPort(host, strconv.Itoa(int(port))) +} diff --git a/proxy/vless/client.go b/proxy/vless/client.go index 6775fda..9d5aac3 100644 --- a/proxy/vless/client.go +++ b/proxy/vless/client.go @@ -2,33 +2,55 @@ package vless import ( "encoding/binary" - "encoding/hex" "errors" "io" "io/ioutil" "net" - "strings" "github.com/nadoo/glider/pool" "github.com/nadoo/glider/proxy" ) -const Version byte = 0 +// NewVLessDialer returns a vless proxy dialer. +func NewVLessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewVLess(s, dialer, nil) +} -// CMD types. -const ( - CmdTCP byte = 1 - CmdUDP byte = 2 -) +// Addr returns forwarder's address. +func (s *VLess) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} -// Conn is a vless client connection. -type Conn struct { +// Dial connects to the address addr on the network net via the proxy. +func (s *VLess) Dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial("tcp", s.addr) + if err != nil { + return nil, err + } + return NewClientConn(rc, s.uuid, network, addr) +} + +// DialUDP connects to the given address via the proxy. +func (s *VLess) 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) + return pkc, nil, nil +} + +// ClientConn is a vless client connection. +type ClientConn struct { net.Conn rcved bool } -// ClientConn returns a new vless client conn. -func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error) { +// NewClientConn returns a new vless client conn. +func NewClientConn(c net.Conn, uuid [16]byte, network, target string) (*ClientConn, error) { atyp, addr, port, err := ParseAddr(target) if err != nil { return nil, err @@ -45,7 +67,7 @@ func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error if network == "udp" { cmd = CmdUDP } - buf.WriteByte(cmd) // cmd + buf.WriteByte(byte(cmd)) // cmd // target err = binary.Write(buf, binary.BigEndian, uint16(port)) // port @@ -56,10 +78,10 @@ func ClientConn(c net.Conn, uuid [16]byte, network, target string) (*Conn, error buf.Write(addr) //addr _, err = c.Write(buf.Bytes()) - return &Conn{Conn: c}, err + return &ClientConn{Conn: c}, err } -func (c *Conn) Read(b []byte) (n int, err error) { +func (c *ClientConn) Read(b []byte) (n int, err error) { if !c.rcved { buf := pool.GetBuffer(2) defer pool.PutBuffer(buf) @@ -81,14 +103,3 @@ func (c *Conn) Read(b []byte) (n int, err error) { return c.Conn.Read(b) } - -// StrToUUID converts string to uuid. -// s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8" -func StrToUUID(s string) (uuid [16]byte, err error) { - b := []byte(strings.Replace(s, "-", "", -1)) - if len(b) != 32 { - return uuid, errors.New("invalid UUID: " + s) - } - _, err = hex.Decode(uuid[:], b) - return -} diff --git a/proxy/vless/packet.go b/proxy/vless/packet.go index c649f61..8929f3f 100644 --- a/proxy/vless/packet.go +++ b/proxy/vless/packet.go @@ -9,15 +9,11 @@ import ( "github.com/nadoo/glider/pool" ) -// PktConn . -type PktConn struct { - net.Conn -} +// PktConn is a udp Packet.Conn. +type PktConn struct{ net.Conn } // NewPktConn returns a PktConn. -func NewPktConn(c net.Conn) *PktConn { - return &PktConn{Conn: c} -} +func NewPktConn(c net.Conn) *PktConn { return &PktConn{Conn: c} } // ReadFrom implements the necessary function of net.PacketConn. // TODO: we know that we use it in proxy.RelayUDP and the length of b is enough, check it later. @@ -35,7 +31,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { // Payload n, err := io.ReadFull(pc.Conn, b[:length]) if err != nil { - return 0, nil, err + return n, nil, err } return n, nil, nil diff --git a/proxy/vless/server.go b/proxy/vless/server.go new file mode 100644 index 0000000..54b556f --- /dev/null +++ b/proxy/vless/server.go @@ -0,0 +1,150 @@ +package vless + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net" + "strings" + + "github.com/nadoo/glider/log" + "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" +) + +// NewVLessServer returns a vless proxy server. +func NewVLessServer(s string, p proxy.Proxy) (proxy.Server, error) { + return NewVLess(s, nil, p) +} + +// ListenAndServe listen and serves connections. +func (s *VLess) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("[vless] failed to listen on %s: %v", s.addr, err) + return + } + defer l.Close() + + log.F("[vless] listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[vless] failed to accept: %v", err) + continue + } + + go s.Serve(c) + } +} + +// Serve serves a connection. +func (s *VLess) Serve(c net.Conn) { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + c = NewServerConn(c) + _, err := s.readHeader(c) + if err != nil { + log.F("[vless] verify header error: %v", err) + return + } + + tgt, err := ReadAddrString(c) + if err != nil { + log.F("[vless] get target error: %v", err) + return + } + + dialer := s.proxy.NextDialer(tgt) + rc, err := dialer.Dial("tcp", tgt) + if err != nil { + log.F("[vless] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + return + } + defer rc.Close() + + log.F("[vless] %s <-> %s via %s", c.RemoteAddr(), tgt, dialer.Addr()) + + if err = proxy.Relay(c, rc); err != nil { + log.F("[vless] %s <-> %s via %s, relay error: %v", c.RemoteAddr(), tgt, dialer.Addr(), err) + // record remote conn failure only + if !strings.Contains(err.Error(), s.addr) { + s.proxy.Record(dialer, false) + } + } +} + +func (s *VLess) readHeader(r io.Reader) (CmdType, error) { + buf := pool.GetBuffer(16) + defer pool.PutBuffer(buf) + + // ver + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, fmt.Errorf("get version error: %v", err) + } + + if buf[0] != Version { + return CmdErr, fmt.Errorf("version %d not supported", buf[0]) + } + + // uuid + if _, err := io.ReadFull(r, buf[:16]); err != nil { + return CmdErr, fmt.Errorf("get uuid error: %v", err) + } + + if !bytes.Equal(s.uuid[:], buf) { + return CmdErr, fmt.Errorf("auth failed, client id: %02x", buf[:16]) + } + + // addLen + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, fmt.Errorf("get addon length error: %v", err) + } + + // ignore addons + if addLen := int64(buf[0]); addLen > 0 { + proxy.CopyN(ioutil.Discard, r, addLen) + } + + // cmd + if _, err := io.ReadFull(r, buf[:1]); err != nil { + return CmdErr, fmt.Errorf("get cmd error: %v", err) + } + + return CmdType(buf[0]), nil +} + +// ServerConn is a vless client connection. +type ServerConn struct { + net.Conn + sent bool +} + +// NewServerConn returns a new vless client conn. +func NewServerConn(c net.Conn) *ServerConn { + return &ServerConn{Conn: c} +} + +func (c *ServerConn) Write(b []byte) (int, error) { + if !c.sent { + buf := pool.GetWriteBuffer() + defer pool.PutWriteBuffer(buf) + + buf.WriteByte(Version) // ver + buf.WriteByte(0) // addonLen + + buf.Write(b) + c.sent = true + + n, err := c.Conn.Write(buf.Bytes()) + return n - 2, err + } + + return c.Conn.Write(b) +} diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index 6c18a96..a02b91b 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -1,25 +1,42 @@ package vless import ( - "net" + "encoding/hex" + "errors" "net/url" + "strings" "github.com/nadoo/glider/proxy" ) +// Version of vless protocol. +const Version byte = 0 + +// CmdType is vless cmd type. +type CmdType byte + +// CMD types. +const ( + CmdErr CmdType = 0 + CmdTCP CmdType = 1 + CmdUDP CmdType = 2 +) + // VLess struct. type VLess struct { dialer proxy.Dialer + proxy proxy.Proxy addr string uuid [16]byte } func init() { proxy.RegisterDialer("vless", NewVLessDialer) + proxy.RegisterServer("vless", NewVLessServer) } // NewVLess returns a vless proxy. -func NewVLess(s string, d proxy.Dialer) (*VLess, error) { +func NewVLess(s string, d proxy.Dialer, p proxy.Proxy) (*VLess, error) { u, err := url.Parse(s) if err != nil { return nil, err @@ -31,43 +48,23 @@ func NewVLess(s string, d proxy.Dialer) (*VLess, error) { return nil, err } - p := &VLess{ + v := &VLess{ dialer: d, + proxy: p, addr: addr, uuid: uuid, } - return p, nil + return v, nil } -// NewVLessDialer returns a vless proxy dialer. -func NewVLessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { - return NewVLess(s, dialer) -} - -// Addr returns forwarder's address. -func (s *VLess) Addr() string { - if s.addr == "" { - return s.dialer.Addr() +// StrToUUID converts string to uuid. +// s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8" +func StrToUUID(s string) (uuid [16]byte, err error) { + b := []byte(strings.Replace(s, "-", "", -1)) + if len(b) != 32 { + return uuid, errors.New("invalid UUID: " + s) } - return s.addr -} - -// Dial connects to the address addr on the network net via the proxy. -func (s *VLess) Dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial("tcp", s.addr) - if err != nil { - return nil, err - } - return ClientConn(rc, s.uuid, network, addr) -} - -// DialUDP connects to the given address via the proxy. -func (s *VLess) 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) - return pkc, nil, nil + _, err = hex.Decode(uuid[:], b) + return } diff --git a/service/dhcpd/dhcpd.go b/service/dhcpd/dhcpd.go index 4600edd..e583a1c 100644 --- a/service/dhcpd/dhcpd.go +++ b/service/dhcpd/dhcpd.go @@ -76,7 +76,7 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { replyIp, err := pool.AssignIP(m.ClientHWAddr) if err != nil { - log.F("[dpcpd] can not assign IP error %s", err) + log.F("[dpcpd] can not assign IP, error %s", err) return } @@ -92,6 +92,10 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { // RFC 2131, Section 4.3.1. IP lease time: MUST dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(leaseTime)), ) + if err != nil { + log.F("[dpcpd] can not create reply message, error %s", err) + return + } if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 { reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val)) diff --git a/service/dhcpd/pool.go b/service/dhcpd/pool.go index 75c0e0e..c06313a 100644 --- a/service/dhcpd/pool.go +++ b/service/dhcpd/pool.go @@ -8,6 +8,7 @@ import ( "time" ) +// Pool is a dhcp pool. type Pool struct { items []*item } @@ -31,6 +32,7 @@ func NewPool(lease time.Duration, start, end net.IP) (*Pool, error) { return &Pool{items: items}, nil } +// AssignIP assigns an ip to mac from dhco pool. func (p *Pool) AssignIP(mac net.HardwareAddr) (net.IP, error) { var ip net.IP for _, item := range p.items {